VirtualBox

Changeset 79800 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Jul 16, 2019 12:06:00 AM (5 years ago)
Author:
vboxsync
Message:

Dhcpd,Main: Working on extending the DHCP server end to cope with new configuration features in IDHCPServer. bugref:9288

Location:
trunk/src/VBox
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/include/DHCPConfigImpl.h

    r79771 r79800  
    235235                                          VirtualBoxBase *pErrorDst);
    236236
     237    /** @name Internal accessors
     238     * @{ */
     239    bool                        i_getInclusive() const RT_NOEXCEPT  { return m_fInclusive; }
     240    DHCPGroupConditionType_T    i_getType() const RT_NOEXCEPT       { return m_enmType; }
     241    com::Utf8Str const         &i_getValue() const RT_NOEXCEPT      { return m_strValue; }
     242    /** @} */
     243
    237244protected:
    238245    /** @name Wrapped IDHCPGroupCondition properties
     
    288295    HRESULT i_saveSettings(settings::DHCPGroupConfig &a_rDst);
    289296    HRESULT i_removeCondition(DHCPGroupCondition *a_pCondition);
     297    void    i_writeDhcpdConfig(xml::ElementNode *a_pElmGroup) RT_OVERRIDE;
    290298
    291299protected:
  • trunk/src/VBox/Main/src-server/DHCPConfigImpl.cpp

    r79778 r79800  
    865865
    866866
     867/**
     868 * Overridden to add a 'name' attribute and emit condition child elements.
     869 */
     870void DHCPGroupConfig::i_writeDhcpdConfig(xml::ElementNode *a_pElmGroup)
     871{
     872    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     873
     874    /* The name attribute: */
     875    a_pElmGroup->setAttribute("name", m_strName);
     876
     877    /*
     878     * Conditions:
     879     */
     880    for (ConditionsIterator it = m_Conditions.begin(); it != m_Conditions.end(); ++it)
     881    {
     882        xml::ElementNode *pElmCondition;
     883        switch ((*it)->i_getType())
     884        {
     885            case DHCPGroupConditionType_MAC:
     886                pElmCondition = a_pElmGroup->createChild("ConditionMAC");
     887                break;
     888            case DHCPGroupConditionType_MACWildcard:
     889                pElmCondition = a_pElmGroup->createChild("ConditionMACWildcard");
     890                break;
     891            case DHCPGroupConditionType_vendorClassID:
     892                pElmCondition = a_pElmGroup->createChild("ConditionVendorClassID");
     893                break;
     894            case DHCPGroupConditionType_vendorClassIDWildcard:
     895                pElmCondition = a_pElmGroup->createChild("ConditionVendorClassIDWildcard");
     896                break;
     897            case DHCPGroupConditionType_userClassID:
     898                pElmCondition = a_pElmGroup->createChild("ConditionUserClassID");
     899                break;
     900            case DHCPGroupConditionType_userClassIDWildcard:
     901                pElmCondition = a_pElmGroup->createChild("ConditionUserClassIDWildcard");
     902                break;
     903            default:
     904                AssertLogRelMsgFailed(("m_enmType=%d\n", (*it)->i_getType()));
     905                continue;
     906        }
     907        pElmCondition->setAttribute("inclusive", (*it)->i_getInclusive());
     908        pElmCondition->setAttribute("value", (*it)->i_getValue());
     909    }
     910
     911    DHCPConfig::i_writeDhcpdConfig(a_pElmGroup);
     912}
     913
     914
    867915HRESULT DHCPGroupConfig::getName(com::Utf8Str &aName)
    868916{
  • trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp

    r79778 r79800  
    3939*   Global Variables                                                                                                             *
    4040*********************************************************************************************************************************/
    41 /*static*/ bool Config::g_fInitializedLog = false;
     41/*static*/ bool         Config::g_fInitializedLog = false;
     42/*static*/ uint32_t     GroupConfig::s_uGroupNo   = 0;
    4243
    4344
     
    5556#endif
    5657
     58    explicit ConfigFileError(xml::Node const *pNode, const char *a_pszMsgFmt, ...)
     59        : RTCError((char *)NULL)
     60    {
     61
     62        i_buildPath(pNode);
     63        m_strMsg.append(": ");
     64
     65        va_list va;
     66        va_start(va, a_pszMsgFmt);
     67        m_strMsg.appendPrintf(a_pszMsgFmt, va);
     68        va_end(va);
     69    }
     70
     71
    5772    ConfigFileError(const char *a_pszMsgFmt, ...)
    5873        : RTCError((char *)NULL)
     
    6782        : RTCError(a_rstrMessage)
    6883    {}
     84
     85private:
     86    void i_buildPath(xml::Node const *pNode)
     87    {
     88        if (pNode)
     89        {
     90            i_buildPath(pNode->getParent());
     91            m_strMsg.append('/');
     92            m_strMsg.append(pNode->getName());
     93            if (pNode->isElement())
     94            {
     95                xml::ElementNode const *pElm = (xml::ElementNode const *)pNode;
     96                for (xml::Node const *pNodeChild = pElm->getFirstChild(); pNodeChild != NULL;
     97                    pNodeChild = pNodeChild->getNextSibiling())
     98                    if (pNodeChild->isAttribute())
     99                    {
     100                        m_strMsg.append("[@");
     101                        m_strMsg.append(pNodeChild->getName());
     102                        m_strMsg.append('=');
     103                        m_strMsg.append(pNodeChild->getValue());
     104                        m_strMsg.append(']');
     105                    }
     106            }
     107        }
     108    }
     109
    69110};
    70111
     
    83124    , m_IPv4PoolFirst()
    84125    , m_IPv4PoolLast()
    85     , m_GlobalOptions()
    86     , m_VMMap()
     126    , m_GlobalConfig()
     127    , m_GroupConfigs()
     128    , m_HostConfigs()
    87129{
    88130}
     
    327369        { "--log-flags",            'f', RTGETOPT_REQ_STRING },
    328370        { "--log-group-settings",   'g', RTGETOPT_REQ_STRING },
     371        { "--relaxed",              'r', RTGETOPT_REQ_NOTHING },
     372        { "--strict",               's', RTGETOPT_REQ_NOTHING },
    329373    };
    330374
     
    339383    const char *pszConfig           = NULL;
    340384    const char *pszComment          = NULL;
     385    bool        fStrict             = true;
    341386
    342387    for (;;)
     
    367412            case 'g':
    368413                pszLogGroupSettings = ValueUnion.psz;
     414                break;
     415
     416            case 'r':
     417                fStrict = false;
     418                break;
     419
     420            case 's':
     421                fStrict = true;
    369422                break;
    370423
     
    376429                break;
    377430
    378             case VINF_GETOPT_NOT_OPTION:
    379                 RTMsgError("Unexpected command line argument: '%s'", ValueUnion.psz);
    380                 return NULL;
    381 
    382431            default:
    383432                RTGetOptPrintError(rc, &ValueUnion);
     
    418467    RTMsgInfo("reading config from '%s'...\n", pszConfig);
    419468    std::unique_ptr<Config> ptrConfig;
    420     ptrConfig.reset(Config::i_read(pszConfig));
     469    ptrConfig.reset(Config::i_read(pszConfig, fStrict));
    421470    if (ptrConfig.get() != NULL)
    422471    {
     
    433482 * @note The release log has is not operational when this method is called.
    434483 */
    435 Config *Config::i_read(const char *pszFileName) RT_NOEXCEPT
     484Config *Config::i_read(const char *pszFileName, bool fStrict) RT_NOEXCEPT
    436485{
    437486    if (pszFileName == NULL || pszFileName[0] == '\0')
     
    468517    try
    469518    {
    470         config->i_parseConfig(doc.getRootElement());
     519        config->i_parseConfig(doc.getRootElement(), fStrict);
    471520    }
    472521    catch (const RTCError &e)
     
    491540
    492541/**
     542 * Helper for retrieving a IPv4 attribute.
     543 *
     544 * @param   pElm            The element to get the attribute from.
     545 * @param   pszAttrName     The name of the attribute
     546 * @param   pAddr           Where to return the address.
     547 * @throws  ConfigFileError
     548 */
     549static void getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTNETADDRIPV4 pAddr)
     550{
     551    const char *pszAttrValue;
     552    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
     553    {
     554        int rc = RTNetStrToIPv4Addr(pszAttrValue, pAddr);
     555        if (RT_SUCCESS(rc))
     556            return;
     557        throw ConfigFileError(pElm, "Attribute %s is not a valid IPv4 address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
     558    }
     559    throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
     560}
     561
     562
     563/**
     564 * Helper for retrieving a MAC address attribute.
     565 *
     566 * @param   pElm            The element to get the attribute from.
     567 * @param   pszAttrName     The name of the attribute
     568 * @param   pMacAddr        Where to return the MAC address.
     569 * @throws  ConfigFileError
     570 */
     571static void getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr)
     572{
     573    const char *pszAttrValue;
     574    if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
     575    {
     576        int rc = RTNetStrToMacAddr(pszAttrValue, pMacAddr);
     577        if (RT_SUCCESS(rc) && rc != VWRN_TRAILING_CHARS)
     578            return;
     579        throw ConfigFileError(pElm, "attribute %s is not a valid MAC address: '%s' -> %Rrc", pszAttrName, pszAttrValue, rc);
     580    }
     581    throw ConfigFileError(pElm, "Required %s attribute missing", pszAttrName);
     582}
     583
     584
     585/**
    493586 * Internal worker for i_read() that parses the root element and everything
    494587 * below it.
    495588 *
    496  * @param   pElmRoot    The root element.
     589 * @param   pElmRoot            The root element.
     590 * @param   fStrict             Set if we're in strict mode, clear if we just
     591 *                              want to get on with it if we can.
    497592 * @throws  std::bad_alloc, ConfigFileError
    498593 */
    499 void Config::i_parseConfig(const xml::ElementNode *pElmRoot)
     594void Config::i_parseConfig(const xml::ElementNode *pElmRoot, bool fStrict)
    500595{
    501596    /*
     
    510605        throw ConfigFileError("Unexpected root element '%s'", pElmRoot->getName());
    511606
    512     i_parseServer(pElmRoot);
     607    i_parseServer(pElmRoot, fStrict);
    513608
    514609#if 0 /** @todo convert to LogRel2 stuff */
     
    539634 *
    540635 * @param   pElmServer          The DHCPServer element.
     636 * @param   fStrict             Set if we're in strict mode, clear if we just
     637 *                              want to get on with it if we can.
    541638 * @throws  std::bad_alloc, ConfigFileError
    542639 */
    543 void Config::i_parseServer(const xml::ElementNode *pElmServer)
     640void Config::i_parseServer(const xml::ElementNode *pElmServer, bool fStrict)
    544641{
    545642    /*
     
    588685    }
    589686
    590     i_getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
    591     i_getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
    592     i_getIPv4AddrAttribute(pElmServer, "lowerIP", &m_IPv4PoolFirst);
    593     i_getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
     687    ::getIPv4AddrAttribute(pElmServer, "IPAddress", &m_IPv4Address);
     688    ::getIPv4AddrAttribute(pElmServer, "networkMask", &m_IPv4Netmask);
     689    ::getIPv4AddrAttribute(pElmServer, "lowerIP", &m_IPv4PoolFirst);
     690    ::getIPv4AddrAttribute(pElmServer, "upperIP", &m_IPv4PoolLast);
    594691
    595692    /*
     
    600697    while ((pElmChild = it.forAllNodes()) != NULL)
    601698    {
     699        /* Global options: */
     700        if (pElmChild->nameEquals("Options"))
     701            m_GlobalConfig.initFromXml(pElmChild, fStrict);
     702        /* Group w/ options: */
     703        else if (pElmChild->nameEquals("Group"))
     704        {
     705            std::unique_ptr<GroupConfig> ptrGroup(new GroupConfig());
     706            ptrGroup->initFromXml(pElmChild, fStrict);
     707            if (m_GroupConfigs.find(ptrGroup->getGroupName()) == m_GroupConfigs.end())
     708            {
     709                m_GroupConfigs[ptrGroup->getGroupName()] = ptrGroup.get();
     710                ptrGroup.release();
     711            }
     712            else if (!fStrict)
     713                LogRelFunc(("Ignoring duplicate group name: %s", ptrGroup->getGroupName().c_str()));
     714            else
     715                throw ConfigFileError("Duplicate group name: %s", ptrGroup->getGroupName().c_str());
     716        }
    602717        /*
    603          * Global options
    604          */
    605         if (pElmChild->nameEquals("Options"))
    606             i_parseGlobalOptions(pElmChild);
    607         /*
    608          * Per-VM configuration
     718         * MAC address and per VM NIC configurations:
    609719         */
    610720        else if (pElmChild->nameEquals("Config"))
    611             i_parseVMConfig(pElmChild);
     721        {
     722            std::unique_ptr<HostConfig> ptrHost(new HostConfig());
     723            ptrHost->initFromXml(pElmChild, fStrict);
     724            if (m_HostConfigs.find(ptrHost->getMACAddress()) == m_HostConfigs.end())
     725            {
     726                m_HostConfigs[ptrHost->getMACAddress()] = ptrHost.get();
     727                ptrHost.release();
     728            }
     729            else if (!fStrict)
     730                LogRelFunc(("Ignorining duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress()));
     731            else
     732                throw ConfigFileError("Duplicate MAC address (Config): %RTmac", &ptrHost->getMACAddress());
     733        }
     734        else if (!fStrict)
     735            LogRel(("Ignoring unexpected DHCPServer child: %s\n", pElmChild->getName()));
    612736        else
    613             LogRel(("Ignoring unexpected DHCPServer child: %s\n", pElmChild->getName()));
    614     }
    615 }
    616 
    617 
    618 /**
    619  * Internal worker for parsing the elements under /DHCPServer/Options/.
    620  *
    621  * @param   pElmServer          The <Options> element.
     737            throw ConfigFileError("Unexpected DHCPServer child <%s>'", pElmChild->getName());
     738    }
     739}
     740
     741
     742/**
     743 * Internal worker for parsing \<Option\> elements found under
     744 * /DHCPServer/Options/, /DHCPServer/Group/ and /DHCPServer/Config/.
     745 *
     746 * @param   pElmOption          An \<Option\> element.
    622747 * @throws  std::bad_alloc, ConfigFileError
    623748 */
    624 void Config::i_parseGlobalOptions(const xml::ElementNode *options)
    625 {
    626     xml::NodesLoop it(*options);
    627     const xml::ElementNode *pElmChild;
    628     while ((pElmChild = it.forAllNodes()) != NULL)
    629     {
    630         if (pElmChild->nameEquals("Option"))
    631             i_parseOption(pElmChild, m_GlobalOptions);
    632         else
    633             throw ConfigFileError("Unexpected element <%s>", pElmChild->getName());
    634     }
    635 }
    636 
    637 
    638 /**
    639  * Internal worker for parsing the elements under /DHCPServer/Config/.
    640  *
    641  * VM Config entries are generated automatically from VirtualBox.xml
    642  * with the MAC fetched from the VM config.  The client id is nowhere
    643  * in the picture there, so VM config is indexed with plain RTMAC, not
    644  * ClientId (also see getOptions below).
    645  *
    646  * @param   pElmServer          The <Config> element.
    647  * @throws  std::bad_alloc, ConfigFileError
    648  */
    649 void Config::i_parseVMConfig(const xml::ElementNode *pElmConfig)
    650 {
    651     /*
    652      * Attributes:
    653      */
    654     /* The MAC address: */
    655     RTMAC MacAddr;
    656     i_getMacAddressAttribute(pElmConfig, "MACAddress", &MacAddr);
    657 
    658     vmmap_t::iterator vmit( m_VMMap.find(MacAddr) );
    659     if (vmit != m_VMMap.end())
    660         throw ConfigFileError("Duplicate Config for MACAddress %RTmac", &MacAddr);
    661 
    662     optmap_t &vmopts = m_VMMap[MacAddr];
    663 
    664     /* Name - optional: */
    665     const char *pszName = NULL;
    666     if (pElmConfig->getAttributeValue("name", &pszName))
    667     {
    668         /** @todo */
    669     }
    670 
    671     /* Fixed IP address assignment - optional: */
    672     if (pElmConfig->findAttribute("FixedIPAddress") != NULL)
    673     {
    674         /** @todo */
    675     }
    676 
    677     /*
    678      * Process the children.
    679      */
    680     xml::NodesLoop it(*pElmConfig);
    681     const xml::ElementNode *pElmChild;
    682     while ((pElmChild = it.forAllNodes()) != NULL)
    683         if (pElmChild->nameEquals("Option"))
    684             i_parseOption(pElmChild, vmopts);
    685         else
    686             throw ConfigFileError("Unexpected element '%s' under '%s'", pElmChild->getName(), pElmConfig->getName());
    687 }
    688 
    689 
    690 /**
    691  * Internal worker for parsing <Option> elements found under
    692  * /DHCPServer/Options/ and /DHCPServer/Config/
    693  *
    694  * @param   pElmServer          The <Option> element.
    695  * @param   optmap              The option map to add the option to.
    696  * @throws  std::bad_alloc, ConfigFileError
    697  */
    698 void Config::i_parseOption(const xml::ElementNode *pElmOption, optmap_t &optmap)
     749void ConfigLevelBase::i_parseOption(const xml::ElementNode *pElmOption)
    699750{
    700751    /* The 'name' attribute: */
    701752    const char *pszName;
    702753    if (!pElmOption->getAttributeValue("name", &pszName))
    703         throw ConfigFileError("missing option name");
     754        throw ConfigFileError(pElmOption, "missing option name");
    704755
    705756    uint8_t u8Opt;
    706757    int rc = RTStrToUInt8Full(pszName, 10, &u8Opt);
    707758    if (rc != VINF_SUCCESS) /* no warnings either */
    708         throw ConfigFileError("Bad option name '%s': %Rrc", pszName, rc);
     759        throw ConfigFileError(pElmOption, "Bad option name '%s': %Rrc", pszName, rc);
    709760
    710761    /* The opional 'encoding' attribute: */
     
    715766        rc = RTStrToUInt32Full(pszEncoding, 10, &u32Enc);
    716767        if (rc != VINF_SUCCESS) /* no warnings either */
    717             throw ConfigFileError("Bad option encoding '%s': %Rrc", pszEncoding, rc);
     768            throw ConfigFileError(pElmOption, "Bad option encoding '%s': %Rrc", pszEncoding, rc);
    718769
    719770        switch (u32Enc)
     
    723774                break;
    724775            default:
    725                 throw ConfigFileError("Unknown encoding '%s'", pszEncoding);
     776                throw ConfigFileError(pElmOption, "Unknown encoding '%s'", pszEncoding);
    726777        }
    727778    }
     
    735786    DhcpOption *opt = DhcpOption::parse(u8Opt, u32Enc, pszValue);
    736787    if (opt == NULL)
    737         throw ConfigFileError("Bad option '%s' (encoding %u): '%s' ", pszName, u32Enc, pszValue ? pszValue : "");
     788        throw ConfigFileError(pElmOption, "Bad option '%s' (encoding %u): '%s' ", pszName, u32Enc, pszValue ? pszValue : "");
    738789
    739790    /* Add it to the map: */
    740     optmap << opt;
    741 }
    742 
    743 
    744 /**
    745  * Helper for retrieving a IPv4 attribute.
    746  *
    747  * @param   pElm            The element to get the attribute from.
    748  * @param   pszAttrName     The name of the attribute
    749  * @param   pAddr           Where to return the address.
    750  * @throws  ConfigFileError
    751  */
    752 /*static*/ void Config::i_getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTNETADDRIPV4 pAddr)
    753 {
    754     const char *pszAttrValue;
    755     if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
    756     {
    757         int rc = RTNetStrToIPv4Addr(pszAttrValue, pAddr);
    758         if (RT_SUCCESS(rc))
    759             return;
    760         throw ConfigFileError("%s attribute %s is not a valid IPv4 address: '%s' -> %Rrc",
    761                               pElm->getName(), pszAttrName, pszAttrValue, rc);
     791    m_Options << opt;
     792}
     793
     794
     795/**
     796 * Final children parser, handling only \<Option\> and barfing at anything else.
     797 *
     798 * @param   pElmChild           The child element to handle.
     799 * @param   fStrict             Set if we're in strict mode, clear if we just
     800 *                              want to get on with it if we can.
     801 * @throws  std::bad_alloc, ConfigFileError
     802 */
     803void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
     804{
     805    if (pElmChild->nameEquals("Option"))
     806    {
     807        try
     808        {
     809            i_parseOption(pElmChild);
     810        }
     811        catch (ConfigFileError &rXcpt)
     812        {
     813            if (fStrict)
     814                throw rXcpt;
     815            LogRelFunc(("Ignoring option: %s\n", rXcpt.what()));
     816        }
     817    }
     818    else if (!fStrict)
     819    {
     820        ConfigFileError Dummy(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
     821        LogRelFunc(("%s\n", Dummy.what()));
    762822    }
    763823    else
    764         throw ConfigFileError("Required %s attribute missing on element %s", pszAttrName, pElm->getName());
    765 }
    766 
    767 
    768 /**
    769  * Helper for retrieving a MAC address attribute.
    770  *
    771  * @param   pElm            The element to get the attribute from.
    772  * @param   pszAttrName     The name of the attribute
    773  * @param   pMacAddr        Where to return the MAC address.
    774  * @throws  ConfigFileError
    775  */
    776 /*static*/ void Config::i_getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr)
    777 {
    778     const char *pszAttrValue;
    779     if (pElm->getAttributeValue(pszAttrName, &pszAttrValue))
    780     {
    781         int rc = RTNetStrToMacAddr(pszAttrValue, pMacAddr);
    782         if (RT_SUCCESS(rc) && rc != VWRN_TRAILING_CHARS)
    783             return;
    784         throw ConfigFileError("%s attribute %s is not a valid MAC address: '%s' -> %Rrc",
    785                               pElm->getName(), pszAttrName, pszAttrValue, rc);
    786     }
     824        throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
     825}
     826
     827
     828/**
     829 * Base class initialization taking a /DHCPServer/Options, /DHCPServer/Group or
     830 * /DHCPServer/Config element as input and handling common attributes as well as
     831 * any \<Option\> children.
     832 *
     833 * @param   pElmConfig          The configuration element to parse.
     834 * @param   fStrict             Set if we're in strict mode, clear if we just
     835 *                              want to get on with it if we can.
     836 * @throws  std::bad_alloc, ConfigFileError
     837 */
     838void ConfigLevelBase::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
     839{
     840    /*
     841     * Common attributes:
     842     */
     843    if (!pElmConfig->getAttributeValue("secMinLeaseTime", &m_secMinLeaseTime))
     844        m_secMinLeaseTime = 0;
     845    if (!pElmConfig->getAttributeValue("secDefaultLeaseTime", &m_secDefaultLeaseTime))
     846        m_secDefaultLeaseTime = 0;
     847    if (!pElmConfig->getAttributeValue("secMaxLeaseTime", &m_secMaxLeaseTime))
     848        m_secMaxLeaseTime = 0;
     849
     850    /*
     851     * Parse children.
     852     */
     853    xml::NodesLoop it(*pElmConfig);
     854    const xml::ElementNode *pElmChild;
     855    while ((pElmChild = it.forAllNodes()) != NULL)
     856        i_parseChild(pElmChild, fStrict);
     857}
     858
     859
     860/**
     861 * Internal worker for parsing the elements under /DHCPServer/Options/.
     862 *
     863 * @param   pElmOptions         The <Options> element.
     864 * @param   fStrict             Set if we're in strict mode, clear if we just
     865 *                              want to get on with it if we can.
     866 * @throws  std::bad_alloc, ConfigFileError
     867 */
     868void GlobalConfig::initFromXml(const xml::ElementNode *pElmOptions, bool fStrict)
     869{
     870    ConfigLevelBase::initFromXml(pElmOptions, fStrict);
     871}
     872
     873
     874/**
     875 * Overrides base class to handle the condition elements under \<Group\>.
     876 *
     877 * @param   pElmChild           The child element.
     878 * @param   fStrict             Set if we're in strict mode, clear if we just
     879 *                              want to get on with it if we can.
     880 * @throws  std::bad_alloc, ConfigFileError
     881 */
     882void GroupConfig::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict)
     883{
     884    if (pElmChild->nameEquals("ConditionMAC"))
     885    { }
     886    else if (pElmChild->nameEquals("ConditionMACWildcard"))
     887    { }
     888    else if (pElmChild->nameEquals("ConditionVendorClassID"))
     889    { }
     890    else if (pElmChild->nameEquals("ConditionVendorClassIDWildcard"))
     891    { }
     892    else if (pElmChild->nameEquals("ConditionUserClassID"))
     893    { }
     894    else if (pElmChild->nameEquals("ConditionUserClassIDWildcard"))
     895    { }
    787896    else
    788         throw ConfigFileError("Required %s attribute missing on element %s", pszAttrName, pElm->getName());
     897    {
     898        ConfigLevelBase::i_parseChild(pElmChild, fStrict);
     899        return;
     900    }
     901}
     902
     903
     904/**
     905 * Internal worker for parsing the elements under /DHCPServer/Group/.
     906 *
     907 * @param   pElmGroup           The \<Group\> element.
     908 * @param   fStrict             Set if we're in strict mode, clear if we just
     909 *                              want to get on with it if we can.
     910 * @throws  std::bad_alloc, ConfigFileError
     911 */
     912void GroupConfig::initFromXml(const xml::ElementNode *pElmGroup, bool fStrict)
     913{
     914    /*
     915     * Attributes:
     916     */
     917    if (!pElmGroup->getAttributeValue("name", m_strName) || m_strName.isEmpty())
     918    {
     919        if (fStrict)
     920            throw ConfigFileError(pElmGroup, "Group as no name or the name is empty");
     921        m_strName.printf("Group#%u", s_uGroupNo++);
     922    }
     923
     924    /*
     925     * Do common initialization (including children).
     926     */
     927    ConfigLevelBase::initFromXml(pElmGroup, fStrict);
     928}
     929
     930
     931/**
     932 * Internal worker for parsing the elements under /DHCPServer/Config/.
     933 *
     934 * VM Config entries are generated automatically from VirtualBox.xml
     935 * with the MAC fetched from the VM config.  The client id is nowhere
     936 * in the picture there, so VM config is indexed with plain RTMAC, not
     937 * ClientId (also see getOptions below).
     938 *
     939 * @param   pElmConfig          The \<Config\> element.
     940 * @param   fStrict             Set if we're in strict mode, clear if we just
     941 *                              want to get on with it if we can.
     942 * @throws  std::bad_alloc, ConfigFileError
     943 */
     944void HostConfig::initFromXml(const xml::ElementNode *pElmConfig, bool fStrict)
     945{
     946    /*
     947     * Attributes:
     948     */
     949    /* The MAC address: */
     950    ::getMacAddressAttribute(pElmConfig, "MACAddress", &m_MACAddress);
     951
     952    /* Name - optional: */
     953    if (!pElmConfig->getAttributeValue("name", m_strName))
     954        m_strName.printf("MAC:%RTmac", m_MACAddress);
     955
     956    /* Fixed IP address assignment - optional: */
     957    const char *pszFixedAddress = pElmConfig->findAttributeValue("FixedIPAddress");
     958    if (!pszFixedAddress || *RTStrStripL(pszFixedAddress) == '\0')
     959        m_fHaveFixedAddress = false;
     960    else
     961    {
     962        m_fHaveFixedAddress = false;
     963        ::getIPv4AddrAttribute(pElmConfig, "FixedIPAddress", &m_FixedAddress);
     964    }
     965
     966    /*
     967     * Do common initialization.
     968     */
     969    ConfigLevelBase::initFromXml(pElmConfig, fStrict);
     970}
     971
     972
     973/**
     974 * Assembles a priorities vector of configurations for the client.
     975 *
     976 * @returns a_rRetConfigs for convenience.
     977 * @param   a_rRetConfigs       Where to return the configurations.
     978 * @param   a_ridClient         The client ID.
     979 * @param   a_ridVendorClass    The vendor class ID if present.
     980 * @param   a_ridUserClass      The user class ID if present
     981 */
     982Config::ConfigVec &Config::getConfigsForClient(Config::ConfigVec &a_rRetConfigs, const ClientId &a_ridClient,
     983                                               const OptVendorClassId &a_ridVendorClass,
     984                                               const OptUserClassId &a_ridUserClass) const
     985{
     986    /* Host specific config first: */
     987    HostConfigMap::const_iterator itHost = m_HostConfigs.find(a_ridClient.mac());
     988    if (itHost != m_HostConfigs.end())
     989        a_rRetConfigs.push_back(itHost->second);
     990
     991    /* Groups: */
     992    RT_NOREF(a_ridVendorClass, a_ridUserClass); /* not yet */
     993
     994    /* Global: */
     995    a_rRetConfigs.push_back(&m_GlobalConfig);
     996
     997    return a_rRetConfigs;
    789998}
    790999
     
    7951004 * @returns a_rRetOpts for convenience
    7961005 * @param   a_rRetOpts      Where to put the requested options.
    797  * @param   reqOpts         The requested options.
    798  * @param   id              The client ID.
    799  * @param   idVendorClass   The vendor class ID.
    800  * @param   idUserClass     The user class ID.
     1006 * @param   a_rReqOpts      The requested options.
     1007 * @param   a_rConfigs      Relevant configurations returned by
     1008 *                          Config::getConfigsForClient().
    8011009 *
    8021010 * @throws  std::bad_alloc
    8031011 */
    804 optmap_t &Config::getOptions(optmap_t &a_rRetOpts, const OptParameterRequest &reqOpts, const ClientId &id,
    805                              const OptVendorClassId &idVendorClass /*= OptVendorClassId()*/,
    806                              const OptUserClassId &idUserClass /*= OptUserClassId()*/) const
    807 {
    808     const optmap_t *vmopts = NULL;
    809     vmmap_t::const_iterator vmit( m_VMMap.find(id.mac()) );
    810     if (vmit != m_VMMap.end())
    811         vmopts = &vmit->second;
    812 
    813     RT_NOREF(idVendorClass, idUserClass); /* not yet */
    814 
     1012optmap_t &Config::getOptionsForClient(optmap_t &a_rRetOpts, const OptParameterRequest &a_rReqOpts, ConfigVec &a_rConfigs) const
     1013{
     1014    /*
     1015     * Always supply the subnet:
     1016     */
    8151017    a_rRetOpts << new OptSubnetMask(m_IPv4Netmask);
    8161018
    817     const OptParameterRequest::value_t& reqValue = reqOpts.value();
     1019    /*
     1020     * Try provide the requested options:
     1021     */
     1022    const OptParameterRequest::value_t &reqValue = a_rReqOpts.value();
    8181023    for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
    8191024    {
    820         uint8_t optreq = *itOptReq;
    821         LogRel2((">>> requested option %d (%#x)\n", optreq, optreq));
    822 
    823         if (optreq == OptSubnetMask::optcode)
     1025        uint8_t bOptReq = *itOptReq;
     1026        LogRel2((">>> requested option %d (%#x)\n", bOptReq, bOptReq));
     1027
     1028        if (bOptReq != OptSubnetMask::optcode)
    8241029        {
     1030            bool fFound = false;
     1031            for (size_t i = 0; i < a_rConfigs.size(); i++)
     1032            {
     1033                optmap_t::const_iterator itFound;
     1034                if (a_rConfigs[i]->findOption(bOptReq, itFound)) /* crap interface */
     1035                {
     1036                    LogRel2(("... found in %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
     1037                    a_rRetOpts << itFound->second;
     1038                    fFound = true;
     1039                    break;
     1040                }
     1041            }
     1042            if (!fFound)
     1043                LogRel3(("... not found\n"));
     1044        }
     1045        else
    8251046            LogRel2(("... always supplied\n"));
    826             continue;
    827         }
    828 
    829         if (vmopts != NULL)
    830         {
    831             optmap_t::const_iterator it( vmopts->find(optreq) );
    832             if (it != vmopts->end())
    833             {
    834                 a_rRetOpts << it->second;
    835                 LogRel2(("... found in VM options\n"));
    836                 continue;
    837             }
    838         }
    839 
    840         optmap_t::const_iterator it( m_GlobalOptions.find(optreq) );
    841         if (it != m_GlobalOptions.end())
    842         {
    843             a_rRetOpts << it->second;
    844             LogRel2(("... found in global options\n"));
    845             continue;
    846         }
    847 
    848         LogRel3(("... not found\n"));
    8491047    }
    8501048
  • trunk/src/VBox/NetworkServices/Dhcpd/Config.h

    r79761 r79800  
    3535
    3636/**
     37 * Base configuration
     38 *
     39 * @author bird (2019-07-15)
     40 */
     41class ConfigLevelBase
     42{
     43private:
     44    /** DHCP options. */
     45    optmap_t        m_Options;
     46    /** Minimum lease time, zero means try next level up. */
     47    uint32_t        m_secMinLeaseTime;
     48    /** Default lease time, zero means try next level up. */
     49    uint32_t        m_secDefaultLeaseTime;
     50    /** Maximum lease time, zero means try next level up. */
     51    uint32_t        m_secMaxLeaseTime;
     52
     53public:
     54    ConfigLevelBase()
     55        : m_Options()
     56        , m_secMinLeaseTime(0)
     57        , m_secDefaultLeaseTime(0)
     58        , m_secMaxLeaseTime(0)
     59    { }
     60
     61    virtual ~ConfigLevelBase()
     62    { }
     63
     64    virtual void        initFromXml(xml::ElementNode const *pElm, bool fStrict);
     65    virtual const char *getType() const RT_NOEXCEPT = 0;
     66    virtual const char *getName() const RT_NOEXCEPT = 0;
     67
     68    /**
     69     * Tries to find DHCP option @a bOpt, returning an success indicator and
     70     * iterator to the result.
     71     */
     72    bool            findOption(uint8_t bOpt, optmap_t::const_iterator &a_rItRet) const RT_NOEXCEPT
     73    {
     74        a_rItRet = m_Options.find(bOpt);
     75        return a_rItRet != m_Options.end();
     76    }
     77
     78protected:
     79    void            i_parseOption(const xml::ElementNode *pElmOption);
     80    virtual void    i_parseChild(const xml::ElementNode *pElmChild, bool fStrict);
     81};
     82
     83
     84/**
     85 * Global config
     86 */
     87class GlobalConfig : public ConfigLevelBase
     88{
     89public:
     90    GlobalConfig()
     91        : ConfigLevelBase()
     92    { }
     93    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
     94    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "global"; }
     95    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return "GlobalConfig"; }
     96};
     97
     98
     99#if 0 /* later */
     100/**
     101 * Group membership condition.
     102 */
     103class GroupConditionBase
     104{
     105protected:
     106    /** The value. */
     107    RTCString   m_strValue;
     108
     109public:
     110
     111};
     112#endif
     113
     114
     115/**
     116 * Group config
     117 */
     118class GroupConfig : public ConfigLevelBase
     119{
     120public:
     121    /** The group name. */
     122    RTCString       m_strName;
     123
     124public:
     125    GroupConfig()
     126        : ConfigLevelBase()
     127    {
     128    }
     129
     130    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
     131    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "group"; }
     132    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return m_strName.c_str(); }
     133
     134    /** @name Accessors
     135     * @{ */
     136    RTCString const    &getGroupName() const RT_NOEXCEPT        { return m_strName; }
     137    /** @} */
     138
     139protected:
     140    void                i_parseChild(const xml::ElementNode *pElmChild, bool fStrict) RT_OVERRIDE;
     141    /** Used to name unnamed groups. */
     142    static uint32_t     s_uGroupNo;
     143};
     144
     145
     146/**
     147 * Host (MAC address) specific configuration.
     148 */
     149class HostConfig : public ConfigLevelBase
     150{
     151protected:
     152    /** The MAC address. */
     153    RTMAC           m_MACAddress;
     154    /** Name annotating the entry. */
     155    RTCString       m_strName;
     156    /** Fixed address assignment when m_fHaveFixedAddress is true. */
     157    RTNETADDRIPV4   m_FixedAddress;
     158    /** Set if we have a fixed address asignment. */
     159    bool            m_fHaveFixedAddress;
     160
     161public:
     162    HostConfig()
     163        : ConfigLevelBase()
     164        , m_fHaveFixedAddress(false)
     165    {
     166        RT_ZERO(m_MACAddress);
     167        RT_ZERO(m_FixedAddress);
     168    }
     169
     170    void initFromXml(xml::ElementNode const *pElm, bool fStrict) RT_OVERRIDE;
     171    const char *getType() const RT_NOEXCEPT RT_OVERRIDE { return "host"; }
     172    const char *getName() const RT_NOEXCEPT RT_OVERRIDE { return m_strName.c_str(); }
     173
     174    /** @name Accessors
     175     * @{ */
     176    RTMAC const        &getMACAddress() const RT_NOEXCEPT       { return m_MACAddress; }
     177    /** @} */
     178};
     179
     180
     181/**
    37182 * DHCP server configuration.
    38183 */
    39184class Config
    40185{
    41     /** @todo XXX: also store fixed address assignments, etc? */
    42     typedef std::map<RTMAC, optmap_t> vmmap_t;
     186    /** Group configuration map. */
     187    typedef std::map<RTCString, GroupConfig * > GroupConfigMap;
     188    /** Host configuration map. */
     189    typedef std::map<RTMAC,     HostConfig *  > HostConfigMap;
     190
    43191
    44192    RTCString       m_strHome;          /**< path of ~/.VirtualBox or equivalent, */
     
    59207
    60208
    61     optmap_t        m_GlobalOptions;    /**< Global DHCP option. */
    62     vmmap_t         m_VMMap;            /**< Per MAC address (VM) DHCP options. */
    63     /** @todo r=bird: optmap_t is too narrow for adding configuration options such
    64      *        as max-lease-time, min-lease-time, default-lease-time and such like
    65      *        that does not translate directly to any specific DHCP option. */
    66     /** @todo r=bird: Additionally, I'd like to have a more generic option groups
    67      *        that fits inbetween m_VMMap (mac based) and m_GlobalOptions.
    68      *        Pattern/wildcard matching on MAC address, possibly also client ID,
    69      *        vendor class and user class, including simple lists of these. */
     209    /** The global configuration. */
     210    GlobalConfig    m_GlobalConfig;
     211    /** The group configurations, indexed by group name. */
     212    GroupConfigMap  m_GroupConfigs;
     213    /** The host configurations, indexed by MAC address. */
     214    HostConfigMap   m_HostConfigs;
    70215
    71216    /** Set if we've initialized the log already (via command line). */
     
    109254    /** @} */
    110255
    111     optmap_t           &getOptions(optmap_t &a_rRetOpts, const OptParameterRequest &reqOpts, const ClientId &id,
    112                                    const OptVendorClassId &idVendorClass = OptVendorClassId(),
    113                                    const OptUserClassId &idUserClass = OptUserClassId()) const;
     256    /** Configuration vector. */
     257    typedef std::vector<ConfigLevelBase const *> ConfigVec;
     258    ConfigVec          &getConfigsForClient(ConfigVec &a_rRetConfigs, const ClientId &a_ridClient,
     259                                            const OptVendorClassId &a_ridVendorClass,
     260                                            const OptUserClassId &a_ridUserClass) const;
     261    optmap_t           &getOptionsForClient(optmap_t &a_rRetOpts, const OptParameterRequest &a_rReqOpts,
     262                                            ConfigVec &a_rConfigs) const;
    114263
    115264private:
    116265    /** @name Configuration file reading and parsing
    117266     * @{ */
    118     static Config      *i_read(const char *pszFileName) RT_NOEXCEPT;
    119     void                i_parseConfig(const xml::ElementNode *root);
    120     void                i_parseServer(const xml::ElementNode *server);
    121     void                i_parseGlobalOptions(const xml::ElementNode *options);
    122     void                i_parseVMConfig(const xml::ElementNode *config);
    123     void                i_parseOption(const xml::ElementNode *option, optmap_t &optmap);
    124 
    125     static void         i_getIPv4AddrAttribute(const xml::ElementNode *pElm, const char *pcszAttrName, PRTNETADDRIPV4 pAddr);
    126     static void         i_getMacAddressAttribute(const xml::ElementNode *pElm, const char *pszAttrName, PRTMAC pMacAddr);
     267    static Config      *i_read(const char *pszFilename, bool fStrict) RT_NOEXCEPT;
     268    void                i_parseConfig(const xml::ElementNode *pElmRoot, bool fStrict);
     269    void                i_parseServer(const xml::ElementNode *pElmServer, bool fStrict);
    127270    /** @} */
    128271};
  • trunk/src/VBox/NetworkServices/Dhcpd/DHCPD.cpp

    r79622 r79800  
    242242        return NULL;
    243243
     244    Config::ConfigVec vecConfigs;
     245    m_pConfig->getConfigsForClient(vecConfigs, req.clientId(), OptVendorClassId(req), OptUserClassId(req));
     246
    244247    Binding *b = m_db.allocateBinding(req);
    245248    if (b == NULL)
     
    270273    reply->addOption(OptLeaseTime(b->leaseTime()));
    271274
    272 
    273275    OptParameterRequest optlist(req);
    274276    optmap_t replyOptions;
    275     reply->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
     277    reply->addOptions(m_pConfig->getOptionsForClient(replyOptions, optlist, vecConfigs));
    276278
    277279    // reply->maybeUnicast(req); /** @todo XXX: we reject ciaddr != 0 above */
     
    307309    }
    308310
     311    Config::ConfigVec vecConfigs;
     312    m_pConfig->getConfigsForClient(vecConfigs, req.clientId(), OptVendorClassId(req), OptUserClassId(req));
    309313
    310314    Binding *b = m_db.allocateBinding(req);
     
    314318    }
    315319
    316 
    317320    std::unique_ptr<DhcpServerMessage> ack(i_createMessage(RTNET_DHCP_MT_ACK, req));
    318321
     
    325328    OptParameterRequest optlist(req);
    326329    optmap_t replyOptions;
    327     ack->addOptions(m_pConfig->getOptions(replyOptions, optlist, req.clientId()));
     330    ack->addOptions(m_pConfig->getOptionsForClient(replyOptions, optlist, vecConfigs));
    328331
    329332    /** @todo r=bird: Sec 9.9 in rfc-2132 indicates the server only sends this in NACKs. Test code? */
     
    351354        return NULL;
    352355
    353     const OptParameterRequest params(req);
    354     if (!params.present())
    355         return NULL;
    356 
     356    OptParameterRequest optlist(req);
     357    if (!optlist.present())
     358        return NULL;
     359
     360    Config::ConfigVec vecConfigs;
    357361    optmap_t info;
    358     m_pConfig->getOptions(info, params, req.clientId());
     362    m_pConfig->getOptionsForClient(info, optlist, m_pConfig->getConfigsForClient(vecConfigs, req.clientId(),
     363                                                                                 OptVendorClassId(req), OptUserClassId(req)));
    359364    if (info.empty())
    360365        return NULL;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette