VirtualBox

Ignore:
Timestamp:
Jul 18, 2019 8:31:15 PM (6 years ago)
Author:
vboxsync
Message:

Dhcpd: Implemented forcing and suppessing DHCP option. bugref:9288

Location:
trunk/src/VBox/NetworkServices/Dhcpd
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/NetworkServices/Dhcpd/Config.cpp

    r79825 r79865  
    773773
    774774/**
     775 * Internal worker for parsing \<ForcedOption\> and \<SupressedOption\> elements
     776 * found under /DHCPServer/Options/, /DHCPServer/Group/ and /DHCPServer/Config/.
     777 *
     778 * @param   pElmOption          The element.
     779 * @param   fForced             Whether it's a ForcedOption (true) or
     780 *                              SuppressedOption element.
     781 * @throws  std::bad_alloc, ConfigFileError
     782 */
     783void ConfigLevelBase::i_parseForcedOrSuppressedOption(const xml::ElementNode *pElmOption, bool fForced)
     784{
     785    /* Only a name attribute: */
     786    const char *pszName;
     787    if (!pElmOption->getAttributeValue("name", &pszName))
     788        throw ConfigFileError(pElmOption, "missing option name");
     789
     790    uint8_t u8Opt;
     791    int rc = RTStrToUInt8Full(pszName, 10, &u8Opt);
     792    if (rc != VINF_SUCCESS) /* no warnings either */
     793        throw ConfigFileError(pElmOption, "Bad option name '%s': %Rrc", pszName, rc);
     794
     795    if (fForced)
     796        m_vecForcedOptions.push_back(u8Opt);
     797    else
     798        m_vecSuppressedOptions.push_back(u8Opt);
     799}
     800
     801
     802/**
    775803 * Final children parser, handling only \<Option\> and barfing at anything else.
    776804 *
    777805 * @param   pElmChild           The child element to handle.
    778806 * @param   fStrict             Set if we're in strict mode, clear if we just
    779  *                              want to get on with it if we can.
     807 *                              want to get on with it if we can.  That said,
     808 *                              the caller will catch ConfigFileError exceptions
     809 *                              and ignore them if @a fStrict is @c false.
    780810 * @param   pConfig             The configuration object.
    781811 * @throws  std::bad_alloc, ConfigFileError
     
    783813void ConfigLevelBase::i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig)
    784814{
     815    /*
     816     * Options.
     817     */
    785818    if (pElmChild->nameEquals("Option"))
    786819    {
    787         try
    788         {
    789             i_parseOption(pElmChild);
    790         }
    791         catch (ConfigFileError &rXcpt)
    792         {
    793             if (fStrict)
    794                 throw rXcpt;
    795             LogRelFunc(("Ignoring option: %s\n", rXcpt.what()));
    796         }
    797     }
    798     else if (!fStrict)
    799     {
    800         ConfigFileError Dummy(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
    801         LogRelFunc(("%s\n", Dummy.what()));
    802     }
    803     else
    804         throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
    805     RT_NOREF(pConfig);
     820        i_parseOption(pElmChild);
     821        return;
     822    }
     823
     824    /*
     825     * Forced and supressed options.
     826     */
     827    bool const fForced = pElmChild->nameEquals("ForcedOption");
     828    if (fForced || pElmChild->nameEquals("SuppressedOption"))
     829    {
     830        i_parseForcedOrSuppressedOption(pElmChild, fForced);
     831        return;
     832    }
     833
     834    /*
     835     * What's this?
     836     */
     837    throw ConfigFileError(pElmChild->getParent(), "Unexpected child '%s'", pElmChild->getName());
     838    RT_NOREF(fStrict, pConfig);
    806839}
    807840
     
    845878    const xml::ElementNode *pElmChild;
    846879    while ((pElmChild = it.forAllNodes()) != NULL)
    847         i_parseChild(pElmChild, fStrict, pConfig);
     880    {
     881        try
     882        {
     883            i_parseChild(pElmChild, fStrict, pConfig);
     884        }
     885        catch (ConfigFileError &rXcpt)
     886        {
     887            if (fStrict)
     888                throw rXcpt;
     889            LogRelFunc(("Ignoring: %s\n", rXcpt.what()));
     890        }
     891    }
    848892}
    849893
     
    11171161{
    11181162    /*
     1163     * The client typcially requests a list of options.  The list is subject to
     1164     * forced and supressed lists on each configuration level in a_rConfig.  To
     1165     * efficiently manage it without resorting to maps, the current code
     1166     * assembles a C-style array of options on the stack that should be returned
     1167     * to the client.
     1168     */
     1169    uint8_t abOptions[256];
     1170    size_t  cOptions = 0;
     1171    size_t  iFirstForced = 255;
     1172#define IS_OPTION_PRESENT(a_bOption)         (memchr(abOptions, (a_bOption), cOptions) != NULL)
     1173#define APPEND_NOT_PRESENT_OPTION(a_bOption) do { \
     1174            AssertLogRelMsgBreak(cOptions < sizeof(abOptions), \
     1175                                 ("a_bOption=%#x abOptions=%.*Rhxs\n", (a_bOption), sizeof(abOptions), &abOptions[0])); \
     1176            abOptions[cOptions++] = (a_bOption); \
     1177        } while (0)
     1178
     1179    const OptParameterRequest::value_t &reqValue = a_rReqOpts.value();
     1180    if (reqValue.size() != 0)
     1181    {
     1182        /* Copy the requested list and append any forced options from the configs: */
     1183        for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
     1184            if (!IS_OPTION_PRESENT(*itOptReq))
     1185                APPEND_NOT_PRESENT_OPTION(*itOptReq);
     1186        iFirstForced = cOptions;
     1187        for (Config::ConfigVec::const_iterator itCfg = a_rConfigs.begin(); itCfg != a_rConfigs.end(); ++itCfg)
     1188        {
     1189            octets_t const &rForced = (*itCfg)->getForcedOptions();
     1190            for (octets_t::const_iterator itOpt = rForced.begin(); itOpt != rForced.end(); ++itOpt)
     1191                if (!IS_OPTION_PRESENT(*itOpt))
     1192                {
     1193                    LogRel3((">>> Forcing option %d (%s)\n", *itOpt, DhcpOption::name(*itOpt)));
     1194                    APPEND_NOT_PRESENT_OPTION(*itOpt);
     1195                }
     1196        }
     1197    }
     1198    else
     1199    {
     1200        /* No options requests, feed the client all available options: */
     1201        for (Config::ConfigVec::const_iterator itCfg = a_rConfigs.begin(); itCfg != a_rConfigs.end(); ++itCfg)
     1202        {
     1203            optmap_t const &rOptions = (*itCfg)->getOptions();
     1204            for (optmap_t::const_iterator itOpt = rOptions.begin(); itOpt != rOptions.end(); ++itOpt)
     1205                if (!IS_OPTION_PRESENT(itOpt->first))
     1206                    APPEND_NOT_PRESENT_OPTION(itOpt->first);
     1207
     1208        }
     1209    }
     1210
     1211    /*
    11191212     * Always supply the subnet:
    11201213     */
    11211214    a_rRetOpts << new OptSubnetMask(m_IPv4Netmask);
    11221215
    1123     /** @todo If a_rReqOpts is not present, provide the sum of all options in
    1124      *        a_rConfigs like ics says it does. */
    1125     /** @todo Look thru a_rConfigs for forced options, maybe we do it by using
    1126      *        DHCP option 55, and merging these into a_rReqOpts. */
    1127     /** @todo Have a way to mute options, i.e. break out of the inner search
    1128      *        loop below. Maybe using 'Suppress' encoding? */
    1129 
    1130     /*
    1131      * Try provide the requested options:
    1132      */
    1133     const OptParameterRequest::value_t &reqValue = a_rReqOpts.value();
    1134     for (octets_t::const_iterator itOptReq = reqValue.begin(); itOptReq != reqValue.end(); ++itOptReq)
    1135     {
    1136         uint8_t bOptReq = *itOptReq;
    1137         LogRel2((">>> requested option %d (%#x)\n", bOptReq, bOptReq));
     1216    /*
     1217     * Try provide the options we've decided to return.
     1218     */
     1219    for (size_t iOpt = 0; iOpt < cOptions; iOpt++)
     1220    {
     1221        uint8_t const bOptReq = abOptions[iOpt];
     1222        if (iOpt < iFirstForced)
     1223            LogRel2((">>> requested option %d (%s)\n", bOptReq, DhcpOption::name(bOptReq)));
     1224        else
     1225            LogRel2((">>> forced option %d (%s)\n", bOptReq, DhcpOption::name(bOptReq)));
    11381226
    11391227        if (bOptReq != OptSubnetMask::optcode)
     
    11421230            for (size_t i = 0; i < a_rConfigs.size(); i++)
    11431231            {
    1144                 optmap_t::const_iterator itFound;
    1145                 if (a_rConfigs[i]->findOption(bOptReq, itFound)) /* crap interface */
     1232                if (!a_rConfigs[i]->isOptionSuppressed(bOptReq))
    11461233                {
    1147                     LogRel2(("... found in %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
    1148                     a_rRetOpts << itFound->second;
     1234                    optmap_t::const_iterator itFound;
     1235                    if (a_rConfigs[i]->findOption(bOptReq, itFound)) /* crap interface */
     1236                    {
     1237                        LogRel2(("... found in %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
     1238                        a_rRetOpts << itFound->second;
     1239                        fFound = true;
     1240                        break;
     1241                    }
     1242                }
     1243                else
     1244                {
     1245                    LogRel2(("... suppressed by %s (type %s)\n", a_rConfigs[i]->getName(), a_rConfigs[i]->getType()));
    11491246                    fFound = true;
    11501247                    break;
     
    11581255    }
    11591256
     1257#undef IS_OPTION_PRESENT
     1258#undef APPEND_NOT_PRESENT_OPTION
    11601259    return a_rRetOpts;
    11611260}
  • trunk/src/VBox/NetworkServices/Dhcpd/Config.h

    r79824 r79865  
    5454    uint32_t        m_secMaxLeaseTime;
    5555
     56    /** Options forced unsolicited upon the client. */
     57    octets_t        m_vecForcedOptions;
     58    /** Options (typcially from higher up) that should be hidden from the client. */
     59    octets_t        m_vecSuppressedOptions;
     60
    5661public:
    5762    ConfigLevelBase()
     
    6065        , m_secDefaultLeaseTime(0)
    6166        , m_secMaxLeaseTime(0)
     67        , m_vecForcedOptions()
     68        , m_vecSuppressedOptions()
    6269    { }
    6370
     
    7986    }
    8087
     88    /** Checks if @a bOpt is suppressed or not. */
     89    bool            isOptionSuppressed(uint8_t bOpt) const RT_NOEXCEPT
     90    {
     91        return m_vecSuppressedOptions.size() > 0
     92            && memchr(&m_vecSuppressedOptions.front(), bOpt, m_vecSuppressedOptions.size()) != NULL;
     93    }
     94
    8195    /** @name Accessors
    8296     * @{ */
    83     uint32_t        getMinLeaseTime()     const RT_NOEXCEPT { return m_secMinLeaseTime; }
    84     uint32_t        getDefaultLeaseTime() const RT_NOEXCEPT { return m_secDefaultLeaseTime; }
    85     uint32_t        getMaxLeaseTime()     const RT_NOEXCEPT { return m_secMaxLeaseTime; }
     97    uint32_t        getMinLeaseTime()       const RT_NOEXCEPT { return m_secMinLeaseTime; }
     98    uint32_t        getDefaultLeaseTime()   const RT_NOEXCEPT { return m_secDefaultLeaseTime; }
     99    uint32_t        getMaxLeaseTime()       const RT_NOEXCEPT { return m_secMaxLeaseTime; }
     100    octets_t const &getForcedOptions()      const RT_NOEXCEPT { return m_vecForcedOptions; }
     101    octets_t const &getSuppressedOptions()  const RT_NOEXCEPT { return m_vecSuppressedOptions; }
     102    optmap_t const &getOptions()            const RT_NOEXCEPT { return m_Options; }
    86103    /** @} */
    87104
    88105protected:
    89106    void            i_parseOption(const xml::ElementNode *pElmOption);
     107    void            i_parseForcedOrSuppressedOption(const xml::ElementNode *pElmOption, bool fForced);
    90108    virtual void    i_parseChild(const xml::ElementNode *pElmChild, bool fStrict, Config const *pConfig);
    91109};
  • trunk/src/VBox/NetworkServices/Dhcpd/DhcpMessage.cpp

    r79818 r79865  
    400400    for (optmap_t::const_iterator it = m_optmap.begin(); it != m_optmap.end(); ++it)
    401401    {
    402         LogRel3(("encoding option %d\n", it->first));
     402        LogRel3(("encoding option %d (%s)\n", it->first, DhcpOption::name(it->first)));
    403403        DhcpOption &opt = *it->second;
    404404        data << opt;
  • trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.cpp

    r79845 r79865  
    285285
    286286
    287 DhcpOption *DhcpOption::parse(uint8_t aOptCode, int aEnc, const char *pcszValue, int *prc /*= NULL*/)
     287/*static*/ DhcpOption *DhcpOption::parse(uint8_t aOptCode, int aEnc, const char *pcszValue, int *prc /*= NULL*/)
    288288{
    289289    int rcIgn;
     
    402402    }
    403403}
     404
     405
     406/**
     407 * Gets the option name (simply "unknown" if not known) for logging purposes.
     408 */
     409/*static*/ const char *DhcpOption::name(uint8_t aOptCode)
     410{
     411    switch (aOptCode)
     412    {
     413#define HANDLE(a_OptClass) \
     414        case a_OptClass::optcode: \
     415            return &#a_OptClass[3]
     416
     417        HANDLE(OptSubnetMask);                  // 1
     418        HANDLE(OptTimeOffset);                  // 2
     419        HANDLE(OptRouters);                     // 3
     420        HANDLE(OptTimeServers);                 // 4
     421        HANDLE(OptNameServers);                 // 5
     422        HANDLE(OptDNSes);                       // 6
     423        HANDLE(OptLogServers);                  // 7
     424        HANDLE(OptCookieServers);               // 8
     425        HANDLE(OptLPRServers);                  // 9
     426        HANDLE(OptImpressServers);              // 10
     427        HANDLE(OptResourceLocationServers);     // 11
     428        HANDLE(OptHostName);                    // 12
     429        HANDLE(OptBootFileSize);                // 13
     430        HANDLE(OptMeritDumpFile);               // 14
     431        HANDLE(OptDomainName);                  // 15
     432        HANDLE(OptSwapServer);                  // 16
     433        HANDLE(OptRootPath);                    // 17
     434        HANDLE(OptExtensionPath);               // 18
     435        HANDLE(OptIPForwarding);                // 19
     436        HANDLE(OptNonLocalSourceRouting);       // 20
     437        HANDLE(OptPolicyFilter);                // 21
     438        HANDLE(OptMaxDgramReassemblySize);      // 22
     439        HANDLE(OptDefaultIPTTL);                // 23
     440        HANDLE(OptPathMTUAgingTimeout);         // 24
     441        HANDLE(OptPathMTUPlateauTable);         // 25
     442        HANDLE(OptInterfaceMTU);                // 26
     443        HANDLE(OptAllSubnetsAreLocal);          // 27
     444        HANDLE(OptBroadcastAddress);            // 28
     445        HANDLE(OptPerformMaskDiscovery);        // 29
     446        HANDLE(OptMaskSupplier);                // 30
     447        HANDLE(OptPerformRouterDiscovery);      // 31
     448        HANDLE(OptRouterSolicitationAddress);   // 32
     449        HANDLE(OptStaticRoute);                 // 33
     450        HANDLE(OptTrailerEncapsulation);        // 34
     451        HANDLE(OptARPCacheTimeout);             // 35
     452        HANDLE(OptEthernetEncapsulation);       // 36
     453        HANDLE(OptTCPDefaultTTL);               // 37
     454        HANDLE(OptTCPKeepaliveInterval);        // 38
     455        HANDLE(OptTCPKeepaliveGarbage);         // 39
     456        HANDLE(OptNISDomain);                   // 40
     457        HANDLE(OptNISServers);                  // 41
     458        HANDLE(OptNTPServers);                  // 42
     459        HANDLE(OptVendorSpecificInfo);          // 43
     460        HANDLE(OptNetBIOSNameServers);          // 44
     461        HANDLE(OptNetBIOSDatagramServers);      // 45
     462        HANDLE(OptNetBIOSNodeType);             // 46
     463        HANDLE(OptNetBIOSScope);                // 47
     464        HANDLE(OptXWindowsFontServers);         // 48
     465        HANDLE(OptXWindowsDisplayManager);      // 49
     466        HANDLE(OptRequestedAddress);            // 50
     467        HANDLE(OptLeaseTime);                   // 51
     468        //HANDLE(OptOptionOverload);              // 52
     469        HANDLE(OptMessageType);                 // 53
     470        HANDLE(OptServerId);                    // 54
     471        HANDLE(OptParameterRequest);            // 55
     472        HANDLE(OptMessage);                     // 56
     473        HANDLE(OptMaxDHCPMessageSize);          // 57
     474        HANDLE(OptRenewalTime);                 // 58
     475        HANDLE(OptRebindingTime);               // 59
     476        HANDLE(OptVendorClassId);               // 60
     477        HANDLE(OptClientId);                    // 61
     478        HANDLE(OptNetWareIPDomainName);         // 62
     479        HANDLE(OptNetWareIPInformation);        // 63
     480        HANDLE(OptNISPlusDomain);               // 64
     481        HANDLE(OptNISPlusServers);              // 65
     482        HANDLE(OptTFTPServerName);              // 66
     483        HANDLE(OptBootfileName);                // 67
     484        HANDLE(OptMobileIPHomeAgents);          // 68
     485        HANDLE(OptSMTPServers);                 // 69
     486        HANDLE(OptPOP3Servers);                 // 70
     487        HANDLE(OptNNTPServers);                 // 71
     488        HANDLE(OptWWWServers);                  // 72
     489        HANDLE(OptFingerServers);               // 73
     490        HANDLE(OptIRCServers);                  // 74
     491        HANDLE(OptStreetTalkServers);           // 75
     492        HANDLE(OptSTDAServers);                 // 76
     493        HANDLE(OptUserClassId);                 // 77
     494        HANDLE(OptSLPDirectoryAgent);           // 78 - Only DHCPOptionEncoding_hex
     495        HANDLE(OptSLPServiceScope);             // 79 - Only DHCPOptionEncoding_hex
     496        HANDLE(OptRapidCommit);                 // 80
     497
     498        HANDLE(OptDomainSearch);                // 119 - Only DHCPOptionEncoding_hex
     499
     500#undef HANDLE
     501        default:
     502            return "unknown";
     503    }
     504}
     505
  • trunk/src/VBox/NetworkServices/Dhcpd/DhcpOptions.h

    r79845 r79865  
    6262public:
    6363    static DhcpOption *parse(uint8_t aOptCode, int aEnc, const char *pcszValue, int *prc = NULL);
     64    static const char *name(uint8_t bOptcode);
    6465
    6566public:
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