Ticket #3491: benu.7.16.patch

File benu.7.16.patch, 116.4 KB (added by tomek, 5 years ago)

Patch as contributed (covers many tickets)

  • configure.ac

    diff --git a/configure.ac b/configure.ac
    index 8e6680b..2b0077f 100644
    a b AC_LINK_IFELSE( 
    866866CPPFLAGS=$CPPFLAGS_SAVED
    867867LIBS=$LIBS_SAVED
    868868
     869# Check for MemDB. By default, the software is not build with MemDB support enabled.
     870dhcp_memdb="no"
     871AC_ARG_WITH([dhcp-memdb],
     872  AC_HELP_STRING([--with-dhcp-memdb=yes/no],
     873      [whether enable memdb backend for DHCP lease manager]),
     874      [dhcp_memdb="$withval"])
     875
     876if test "${dhcp_memdb}" = "yes" ; then
     877    AC_DEFINE([HAVE_MEMDB], [1], ["MemDB backend is present"])
     878fi
     879AM_CONDITIONAL(HAVE_MEMDB, test "$dhcp_memdb" != "no")
     880
     881# Add another lease allocator: CUSTOM. Developpers can customize how to pick address.
     882# Check for lease alloc engine. The default lease allocator is iterative
     883AC_ARG_WITH([alloc-engine],
     884    AC_HELP_STRING([--with-alloc-engine],
     885    [Selects configuration engines. Current available options are ITERATIVE (default),
     886    CUSTOM (not implemented yet.)]),
     887    [ALLOC_ENGINE="$withval"],
     888    [ALLOC_ENGINE=ITERATIVE])
     889
     890if test "$ALLOC_ENGINE" = "CUSTOM" ; then
     891    AC_DEFINE([ALLOC_ENGINE_CUSTOM], [1], ["Custom AllocEngine should be used."])
     892fi
     893
     894AM_CONDITIONAL(ALLOC_ENGINE_CUSTOM, test "$ALLOC_ENGINE" = "CUSTOM")
     895
    869896# Check for MySql.  The path to the mysql_config program is given with
    870897# the --with-mysql-config (default to /usr/bin/mysql-config).  By default,
    871898# the software is not built with MySQL support enabled.
    AC_SUBST(AWK) 
    12921319# Kea configuration backend section
    12931320# Currently there are 2 backends available: BUNDY and JSON
    12941321# It is possible that we may extend this to accept additional backends.
     1322
     1323# Added another backend controller: CUSTOM.
    12951324AC_ARG_WITH(kea-config,
    12961325    AC_HELP_STRING([--with-kea-config],
    12971326    [Selects configuration backend; currently available options are: BUNDY (default,
    AC_ARG_WITH(kea-config, 
    13021331
    13031332AM_CONDITIONAL(CONFIG_BACKEND_BUNDY, test "x$CONFIG_BACKEND" = "xBUNDY")
    13041333AM_CONDITIONAL(CONFIG_BACKEND_JSON,   test "x$CONFIG_BACKEND" = "xJSON")
     1334AM_CONDITIONAL(CONFIG_BACKEND_CUSTOM, test "x$CONFIG_BACKEND" = "xCUSTOM")
    13051335
    13061336if test "x$CONFIG_BACKEND" = "xBUNDY"; then
    13071337    AC_DEFINE(CONFIG_BACKEND_BUNDY, 1, [Define to 1 if Kea config was set to BUNDY])
    if test "x$CONFIG_BACKEND" = "xJSON"; then 
    13111341    AC_DEFINE(CONFIG_BACKEND_JSON, 1, [Define to 1 if Kea config was set to JSON])
    13121342fi
    13131343
     1344if test "x$CONFIG_BACKEND" = "xCUSTOM"; then
     1345    AC_DEFINE(CONFIG_BACKEND_CUSTOM, 1, [Define to 1 if Kea config was set to CUSTOM])
     1346fi
     1347
    13141348# Let's sanity check if the specified backend value is allowed
    1315 if test "x$CONFIG_BACKEND" != "xBUNDY" && test "x$CONFIG_BACKEND" != "xJSON"; then
     1349if test "x$CONFIG_BACKEND" != "xBUNDY" && test "x$CONFIG_BACKEND" != "xJSON" && test "x$CONFIG_BACKEND" != "xCUSTOM"; then
    13161350   AC_MSG_ERROR("Invalid configuration backend specified: $CONFIG_BACKEND. The only supported are: BUNDY JSON")
    13171351fi
    13181352
  • src/bin/d2/Makefile.am

    diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
    index ec03164..4eab87c 100644
    a b b10_dhcp_ddns_SOURCES += bundy_d2_controller.cc bundy_d2_controller.h 
    7474else
    7575if CONFIG_BACKEND_JSON
    7676b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
     77else
     78#The CUSTOM BACKEND for d2 has not been implemented. So will use the d2_controller to avoid compilation errors.
     79if CONFIG_BACKEND_CUSTOM
     80b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
     81endif
    7782endif
    7883endif
    7984
  • src/bin/d2/tests/Makefile.am

    diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
    index 9e58756..ecae69a 100644
    a b if CONFIG_BACKEND_JSON 
    9595d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
    9696d2_unittests_SOURCES += d2_controller_unittests.cc
    9797d2_unittests_SOURCES += d_controller_unittests.cc
     98else
     99#As the CUSTOM Backend controller for d2 has not been implemented, use the default d2_controller to avoid compilation errors.
     100#Will add custom_controller and custom_controller_unittests later.
     101if CONFIG_BACKEND_CUSTOM
     102d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
     103d2_unittests_SOURCES += d2_controller_unittests.cc
     104d2_unittests_SOURCES += d_controller_unittests.cc
     105endif
    98106endif
    99107endif
    100108
  • src/bin/dhcp4/Makefile.am

    diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
    index c7f7d5c..4a52d83 100644
    a b AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin 
    55AM_CPPFLAGS += $(BOOST_INCLUDES)
    66
    77AM_CXXFLAGS = $(B10_CXXFLAGS)
     8
    89if USE_CLANGPP
    910# Disable unused parameter warning caused by some Boost headers when compiling with clang
    1011AM_CXXFLAGS += -Wno-unused-parameter
    if CONFIG_BACKEND_JSON 
    6364b10_dhcp4_SOURCES += kea_controller.cc
    6465endif
    6566
     67if CONFIG_BACKEND_CUSTOM
     68AM_CXXFLAGS += -Wno-unused-parameter
     69b10_dhcp4_SOURCES += custom_controller.cc
     70endif
     71
    6672nodist_b10_dhcp4_SOURCES = dhcp4_messages.h dhcp4_messages.cc
    6773EXTRA_DIST += dhcp4_messages.mes
    6874
  • new file src/bin/dhcp4/custom_controller.cc

    diff --git a/src/bin/dhcp4/custom_controller.cc b/src/bin/dhcp4/custom_controller.cc
    new file mode 100644
    index 0000000..4756c34
    - +  
     1/*
     2 * =====================================================================================
     3 *
     4 *       Filename:  custome_controller.cc
     5 *
     6 *    Description:  Controller to config and start the DHCP service.
     7 *
     8 *        Version:  1.0
     9 *        Created:  07/08/2014 03:56:53 PM
     10 *       Revision:  none
     11 *       Compiler:  gcc
     12 *
     13 *         Author:  David Gong (dgong), dgong@benunets.com
     14 *   Organization:  Benu Networks Inc.
     15 *
     16 * =====================================================================================
     17 */
     18
     19#include <config.h>
     20
     21#include <dhcp4/json_config_parser.h>
     22#include <dhcp4/ctrl_dhcp4_srv.h>
     23#include <dhcp4/dhcp4_log.h>
     24#include <exceptions/exceptions.h>
     25
     26namespace isc {
     27namespace dhcp {
     28
     29/// This function initialize the DHCP4 server using configurations from the specified
     30/// filename.
     31void
     32ControlledDhcpv4Srv::init(const std::string& file_name) {
     33    isc_throw(NotImplemented, "ControlledDhcp4Srv::init() for CustomController has not been implemented.");
     34}
     35
     36/// This function cleans up any active connections before closing the DHCP4 server.
     37void ControlledDhcpv4Srv::cleanup() {
     38    isc_throw(NotImplemented, "ControlledDhcp4Srv::cleanup() for CustomController has not been implemented.");
     39}
     40
     41/// This is a logger initialization for CUSTOM controller backend.
     42/// For now, it's just setting log messages to be printed on stdout the same as the JSON /// backend.
     43void Daemon::loggerInit(const char*, bool verbose) {
     44
     45    setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
     46    setenv("B10_LOGGER_ROOT", "kea", 0);
     47    setenv("B10_LOGGER_SEVERITY", (verbose ? "DEBUG":"INFO"), 0);
     48    setenv("B10_LOGGER_DBGLEVEL", "99", 0);
     49    setenv("B10_LOGGER_DESTINATION",  "stdout", 0);
     50    isc::log::initLogger();
     51}
     52
     53};
     54};
  • src/bin/dhcp4/dhcp4.spec

    diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
    index 24fd470..6b72f8b 100644
    a b  
    197197                "item_type": "boolean",
    198198                "item_optional": true,
    199199                "item_default": true
    200             }
     200            },
     201            {
     202                "item_name": "port",
     203                "item_type": "string",
     204                "item_optional": true,
     205                "item_default": "3306"
     206            },
     207            {
     208                "item_name": "timeout",
     209                "item_type": "string",
     210                "item_optional": true,
     211                "item_default": "1"
     212            }
    201213        ]
    202214      },
    203215
  • src/bin/dhcp4/dhcp4_hooks.dox

    diff --git a/src/bin/dhcp4/dhcp4_hooks.dox b/src/bin/dhcp4/dhcp4_hooks.dox
    index ebe2d89..98bfa38 100644
    a b packet processing. Hook points that are not specific to packet processing 
    9393   drop the packet and start processing the next one.  The reason for the drop
    9494   will be logged if logging is set to the appropriate debug level.
    9595
     96@subsection dhcpv4Hookssubnet4Preselect subnet4_preselect
     97- @b Arguments:
     98   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in/out</b>
     99   - name: @b subnet4, type: isc::dhcp::Subnet4Ptr, direction: <b>out</b>
     100
     101- @b Description: this callout is executed before a subnet is selected
     102     for the incoming packet. This callout can select a subnet for the
     103     packet from other database backend (e.g., mysql, memdb), rather
     104     than from the subnet4collection from the cfgmgr.
     105
     106- <b>Skip flag action</b>: If any clalout installed on 'subnet4_preselect'
     107  sets the skip flag, the server will not try to select a subnet for it
     108  again. Packet processing will continue, but if the callout does not select
     109  a subnet already, the lease manager will be unable to assign an IP address.
     110
    96111@subsection dhcpv4HooksSubnet4Select subnet4_select
    97112
    98113 - @b Arguments:
    packet processing. Hook points that are not specific to packet processing 
    156171   sets the skip flag, the server will not update the lease and will
    157172   use old values instead.
    158173
     174@subsection dhcpv4HooksLeasePreRelease lease4_prerelease
     175
     176 - @b Arguments:
     177   - name: @b query4, type: isc::dhcp::Pkt4Ptr, direction: <b>in</b>
     178   - name: @b lease4, type: isc::dhcp::Lease4Ptr, direction: <b>in/out</b>
     179
     180 - @b Description: this callout is executed when the server engine
     181   is about to release a lease, as a result of receiving RELEASE packet.
     182   The callout should try to fetch the lease based on the query4, and
     183   check whether the information in the retrived lease is consistent
     184   with the information in the RELEASE packet. The lease4 argument is
     185   an empty ptr to be filled by the callout.
     186
     187 - <b>Skip flag action</b>: If any callout installed on 'lease4_prerelease'
     188   sets the skip flag, the server will not try to fetch the lease and do
     189   sanity check.
     190
    159191@subsection dhcpv4HooksLeaseRelease lease4_release
    160192
    161193 - @b Arguments:
  • src/bin/dhcp4/dhcp4_messages.mes

    diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
    index 21c54d8..d7bb90c 100644
    a b setting of the flag by a callout instructs the server to drop the packet. 
    122122Server completed all the processing (e.g. may have assigned, updated
    123123or released leases), but the response will not be send to the client.
    124124
     125% DHCP4_HOOK_LEASE4_PRERELEASE_SKIP DHCPv4 lease was not retrived and checked because a callout set the skip flag.
     126This debug message is printed when a callout installed on lease4_prerelease
     127hook point set the skip flag. For this particular hook point, the
     128setting of the flag by a callout instructs the server to not fetch a lease and check the HWaddr, clientid, etc.
     129
    125130% DHCP4_HOOK_LEASE4_RELEASE_SKIP DHCPv4 lease was not released because a callout set the skip flag.
    126131This debug message is printed when a callout installed on lease4_release
    127132hook point set the skip flag. For this particular hook point, the
    of the flag instructs the server to drop the packet. This means that 
    140145the client will not get any response, even though the server processed
    141146client's request and acted on it (e.g. possibly allocated a lease).
    142147
     148% DHCP4_HOOK_SUBNET4_PRESELECT_SKIP no subnet is selected by the DHCP4_SRV, because a callout set skip flag.
     149subnet4_preselect hook point sets the skip flag. For this particular hook point, the setting of the
     150flag instrcts the server not to choose a subnet or even to get the subnet collection, as the subnet4_preselect
     151hook will select a subnet for it.
     152
    143153% DHCP4_HOOK_SUBNET4_SELECT_SKIP no subnet was selected, because a callout set skip flag.
    144154This debug message is printed when a callout installed on the
    145155subnet4_select hook point sets the skip flag. For this particular hook
  • src/bin/dhcp4/dhcp4_srv.cc

    diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
    index ca35aa1..cf39acc 100644
    a b using namespace std; 
    5555struct Dhcp4Hooks {
    5656    int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
    5757    int hook_index_pkt4_receive_;   ///< index for "pkt4_receive" hook point
     58    int hook_index_subnet4_preselect_; /// < index for "subnet4_preselect" hook point
    5859    int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
     60    int hook_index_lease4_prerelease_; /// <index for "lease4_prerelease" hook point
    5961    int hook_index_lease4_release_; ///< index for "lease4_release" hook point
    6062    int hook_index_pkt4_send_;      ///< index for "pkt4_send" hook point
    6163    int hook_index_buffer4_send_;   ///< index for "buffer4_send" hook point
    struct Dhcp4Hooks { 
    6466    Dhcp4Hooks() {
    6567        hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
    6668        hook_index_pkt4_receive_   = HooksManager::registerHook("pkt4_receive");
     69        hook_index_subnet4_preselect_ = HooksManager::registerHook("subnet4_preselect");
    6770        hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
    6871        hook_index_pkt4_send_      = HooksManager::registerHook("pkt4_send");
     72        hook_index_lease4_prerelease_ = HooksManager::registerHook("lease4_prerelease");
    6973        hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
    7074        hook_index_buffer4_send_   = HooksManager::registerHook("buffer4_send");
    7175    }
    Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast, 
    8690                     const bool direct_response_desired)
    8791    : shutdown_(true), alloc_engine_(), port_(port),
    8892      use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
     93      hook_index_subnet4_preselect_(-1),
    8994      hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {
    9095
    9196    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
    Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const bool use_bcast, 
    112117            IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
    113118        }
    114119
     120#ifdef ALLOC_ENGINE_CUSTOM
     121        //If the user chooses to use the CUSTOM LEASE ALLOCATOR, initalize the
     122        //ALLOC_CUSTOM Engine. Otherwise, initialize the default Iterative alloc engine.
     123        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_CUSTOM, 1, false));
     124        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, "Custom Allocator is used to allocate address!");
     125#else
    115126        // Instantiate allocation engine
    116127        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
    117128                                            false /* false = IPv4 */));
     129#endif
    118130
    119131        // Register hook points
    120132        hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
     133        hook_index_subnet4_preselect_ = Hooks.hook_index_subnet4_preselect_;
    121134        hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
    122135        hook_index_pkt4_send_      = Hooks.hook_index_pkt4_send_;
    123136
    Dhcpv4Srv::run() { 
    217230            // processing step would to parse the packet, so skip at this
    218231            // stage means that callouts did the parsing already, so server
    219232            // should skip parsing.
    220             if (callout_handle->getSkip()) {
     233           
     234            // The buffer4_recv() callouts may found that the DHCP message is malformed and thus no need to be further unpacked and processed.
     235            // There we introduce an argument "DROP" to indicate whether to discard this packet directly. Another flag (e.g. set/getDrop()) can
     236            // be introduced in the callout_handle to achieve the same objective.
     237            try{
     238                bool drop = false;
     239                callout_handle->getArgument("DROP", drop);
     240                if(drop)
     241                {
     242                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, "The buffer4_recv() hook indicates that we should drop this packet!");
     243                    continue;
     244                }
     245            }
     246            catch(const NoSuchArgument &ex) {
     247                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, "The buffer4_recv() callouts are supposed to add a DROP argument in the handle!");
     248            }
     249           
     250            if (callout_handle->getSkip()) {
    221251                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_RCVD_SKIP);
    222252                skip_unpack = true;
    223253            }
    Dhcpv4Srv::run() { 
    283313            }
    284314
    285315            callout_handle->getArgument("query4", query);
    286         }
     316        }
    287317
    288318        try {
    289319            switch (query->getType()) {
    Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) { 
    943973        .arg(subnet->toText());
    944974
    945975    // Get client-id option
     976
    946977    ClientIdPtr client_id;
    947978    OptionPtr opt = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
    948979    if (opt) {
    Dhcpv4Srv::processRequest(Pkt4Ptr& request) { 
    12391270
    12401271    /// @todo Uncomment this (see ticket #3116)
    12411272    /// sanityCheck(request, MANDATORY);
    1242 
    12431273    Pkt4Ptr ack = Pkt4Ptr
    12441274        (new Pkt4(DHCPACK, request->getTransid()));
    12451275
    Dhcpv4Srv::processRelease(Pkt4Ptr& release) { 
    12901320    }
    12911321
    12921322    try {
    1293         // Do we have a lease for that particular address?
    1294         Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
    1295 
    1296         if (!lease) {
    1297             // No such lease - bogus release
    1298             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
    1299                       DHCP4_RELEASE_FAIL_NO_LEASE)
    1300                       .arg(client_id ? client_id->toText() : "(no client-id)")
    1301                       .arg(release->getHWAddr() ?
    1302                            release->getHWAddr()->toText() : "(no hwaddr info)")
    1303                       .arg(release->getCiaddr().toText());
    1304             return;
    1305         }
    13061323
    1307         // Does the hardware address match? We don't want one client releasing
    1308         // second client's leases.
    1309         if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
    1310             /// @todo: Print hwaddr from lease as part of ticket #2589
    1311             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
    1312                 .arg(release->getCiaddr().toText())
    1313                 .arg(client_id ? client_id->toText() : "(no client-id)")
    1314                 .arg(release->getHWAddr()->toText());
    1315             return;
    1316         }
     1324        CalloutHandlePtr callout_handle = getCalloutHandle(release);
     1325        Lease4Ptr lease;
     1326        // Here we introduce another hook lease4_prerelease(), so the library developpers can retrive the lease and conduct security
     1327        // check in a custom manner.
     1328        // Execute all callouts registered for lease4_prerelease
     1329        if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_prerelease_)) {
    13171330
    1318         // Does the lease have client-id info? If it has, then check it with what
    1319         // the client sent us.
    1320         if (lease->client_id_ && client_id && *lease->client_id_ != *client_id) {
    1321             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT_ID)
    1322                 .arg(release->getCiaddr().toText())
    1323                 .arg(client_id->toText())
    1324                 .arg(lease->client_id_->toText());
    1325             return;
    1326         }
     1331            // Delete all previous arguments
     1332            callout_handle->deleteAllArguments();
    13271333
    1328         bool skip = false;
     1334            // Pass the original packet
     1335            callout_handle->setArgument("query4", release);
     1336
     1337            // Pass the lease to be updated
     1338            callout_handle->setArgument("lease4", lease);
     1339
     1340            // Call all installed callouts
     1341            HooksManager::callCallouts(Hooks.hook_index_lease4_prerelease_,
     1342                                       *callout_handle);
     1343
     1344            // Callouts decided to skip the next processing step. The next
     1345            // processing step would to check whether the lease exists.
     1346        }
     1347        //Again, if the lease4_prerelease() callouts indicate that this message is malformed, then it is unnecessary to
     1348        //try to retrive and release the lease.
     1349        try {
     1350            bool drop = false;
     1351            callout_handle->getArgument("DROP", drop);
     1352            if(drop)
     1353            {
     1354                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
     1355                        "The buffer4_prerelease() callouts indicate that this message should be dropped directly.");
     1356                return;
     1357            }
     1358        }
     1359        catch(const NoSuchArgument &ex) {
     1360            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, "The buffer4_prerelease() callouts have not set a DROP argument");
     1361        }
     1362        if(callout_handle->getSkip())
     1363        {
     1364            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
     1365                    DHCP4_HOOK_LEASE4_PRERELEASE_SKIP);
     1366            callout_handle->getArgument("lease4", lease);
     1367        }
     1368        //If there is no lease4_prerelease callout, then retrive the lease and check the consistency of the HWAddr/ClientID in the lease
     1369        //and the release request
     1370        else
     1371        {
     1372            lease = LeaseMgrFactory::instance().getLease4(release->getCiaddr());
     1373
     1374            if (!lease) {
     1375            // No such lease - bogus release
     1376                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
     1377                      DHCP4_RELEASE_FAIL_NO_LEASE)
     1378                        .arg(client_id ? client_id->toText() : "(no client-id)")
     1379                        .arg(release->getHWAddr() ?
     1380                        release->getHWAddr()->toText() : "(no hwaddr info)")
     1381                        .arg(release->getCiaddr().toText());
     1382                    return;
     1383            }
     1384
     1385            // Does the hardware address match? We don't want one client releasing
     1386            // second client's leases.
     1387            if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
     1388                /// @todo: Print hwaddr from lease as part of ticket #2589
     1389                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
     1390                    .arg(release->getCiaddr().toText())
     1391                    .arg(client_id ? client_id->toText() : "(no client-id)")
     1392                    .arg(release->getHWAddr()->toText());
     1393                return;
     1394            }
     1395
     1396            // Does the lease have client-id info? If it has, then check it with what
     1397            // the client sent us.
     1398            if (lease->client_id_ && client_id && *lease->client_id_ != *client_id) {
     1399                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_CLIENT_ID)
     1400                    .arg(release->getCiaddr().toText())
     1401                    .arg(client_id->toText())
     1402                    .arg(lease->client_id_->toText());
     1403                return;
     1404            }
     1405        }
     1406       
     1407        bool skip = false;
    13291408
    13301409        // Execute all callouts registered for lease4_release
    13311410        if (HooksManager::calloutsPresent(Hooks.hook_index_lease4_release_)) {
    1332             CalloutHandlePtr callout_handle = getCalloutHandle(release);
    13331411
    13341412            // Delete all previous arguments
    13351413            callout_handle->deleteAllArguments();
    Subnet4Ptr 
    14331511Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
    14341512
    14351513    Subnet4Ptr subnet;
     1514
     1515    // Before trying to select a subnet from the cfgmgr based on the giaddr, ciaddr, incoming interface
     1516    // from the CfgMgr, call all callouts registered at subnet4_preselect to see whether they have alternative
     1517    // preferences and policies to select a subnet4.
     1518
     1519    if (HooksManager::calloutsPresent(hook_index_subnet4_preselect_)) {
     1520        CalloutHandlePtr callout_handle = getCalloutHandle(question);
     1521
     1522        callout_handle->deleteAllArguments();
     1523        callout_handle->setArgument("query4", question);
     1524        callout_handle->setArgument("subnet4", subnet);
     1525
     1526        HooksManager::callCallouts(hook_index_subnet4_preselect_,
     1527                *callout_handle);
     1528
     1529        if(callout_handle->getSkip()) {
     1530            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
     1531                    DHCP4_HOOK_SUBNET4_PRESELECT_SKIP);
     1532            callout_handle->getArgument("subnet4", subnet);
     1533            return subnet;
     1534        }
     1535    }
     1536
    14361537    static const IOAddress notset("0.0.0.0");
    14371538    static const IOAddress bcast("255.255.255.255");
    14381539
    Dhcpv4Srv::accept(const Pkt4Ptr& query) const { 
    15311632bool
    15321633Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
    15331634    try {
    1534         if (pkt->isRelayed()) {
     1635        //As some vendors do not implement the GIAddr and HOPS fields properly (e.g., GIAddr == 0.0.0.0 while HOPS > 0),
     1636        //we introduce a global option to let the administrators to choose whether to accept such DHCP messages or not.
     1637        //This global option is named relay_check_strict_, and is stored in the CfgMgr. Thus before calling the isRelay()
     1638        //function, get this option from the CfgMgr.
     1639        bool relay_check_strict = CfgMgr::instance().relayCheckStrict();
     1640        if (pkt->isRelayed(relay_check_strict)) {
    15351641            return (true);
    15361642        }
    15371643    } catch (const Exception& ex) {
  • src/bin/dhcp4/dhcp4_srv.h

    diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
    index a8f30d7..6defca6 100644
    a b private: 
    705705
    706706    /// Indexes for registered hook points
    707707    int hook_index_pkt4_receive_;
     708    /// Define the index for the newly defined subnet4_preselect() hook.
     709    int hook_index_subnet4_preselect_;
    708710    int hook_index_subnet4_select_;
    709711    int hook_index_pkt4_send_;
    710712};
  • src/bin/dhcp4/json_config_parser.cc

    diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc
    index ddacb7a..3151747 100644
    a b namespace dhcp { 
    451451        parser = new HooksLibrariesParser(config_id);
    452452    } else if (config_id.compare("echo-client-id") == 0) {
    453453        parser = new BooleanParser(config_id, globalContext()->boolean_values_);
     454    // Introduce this relay-check-strict option to let the administrators to choose whether
     455    // to accept DHCP messages that are not relayed but have a HOPS field greater than zero.
     456    } else if (config_id.compare("relay-check-strict") == 0) {
     457        parser = new BooleanParser(config_id, globalContext()->boolean_values_);
    454458    } else if (config_id.compare("dhcp-ddns") == 0) {
    455459        parser = new D2ClientConfigParser(config_id);
    456460    } else {
    void commitGlobalOptions() { 
    471475    try {
    472476        bool echo_client_id = globalContext()->boolean_values_->getParam("echo-client-id");
    473477        CfgMgr::instance().echoClientId(echo_client_id);
     478        //Get the relay-check-strict parameter from the parsed json object, and sets it in the CfgMgr.
     479        bool relay_check_strict = globalContext()->boolean_values_->getParam("relay-check-strict");
     480        CfgMgr::instance().relayCheckStrict(relay_check_strict);
    474481    } catch (...) {
    475482        // Ignore errors. This flag is optional
    476483    }
  • src/bin/dhcp4/tests/Makefile.am

    diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
    index 7437fed..5dc355a 100644
    a b dhcp4_unittests_SOURCES += ../kea_controller.cc 
    123123dhcp4_unittests_SOURCES += kea_controller_unittest.cc
    124124endif
    125125
     126# As the unittests for CUSTOM BACKEND have not been implemented. We will use the KEA controller when
     127# for now, to avoid compilation errors if the CUSTOM BACKEND is selected.
     128if CONFIG_BACKEND_CUSTOM
     129dhcp4_unittests_SOURCES += ../kea_controller.cc
     130dhcp4_unittests_SOURCES += kea_controller_unittest.cc
     131endif
     132
    126133nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
    127134nodist_dhcp4_unittests_SOURCES += marker_file.h test_libraries.h
    128135
  • src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

    diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
    index 839d8fd..97ebac6 100644
    a b TEST_F(Dhcpv4SrvTest, Hooks) { 
    15441544    int hook_index_pkt4_send       = -1;
    15451545    int hook_index_buffer4_send    = -1;
    15461546
     1547    // additional hooks for subnet4_preselect and lease4_prerelease
     1548    int hook_index_subnet4_preselect = -1;
     1549    int hook_index_lease4_prerelease = -1;
     1550
    15471551    // check if appropriate indexes are set
    15481552    EXPECT_NO_THROW(hook_index_buffer4_receive = ServerHooks::getServerHooks()
    15491553                    .getIndex("buffer4_receive"));
    TEST_F(Dhcpv4SrvTest, Hooks) { 
    15581562    EXPECT_NO_THROW(hook_index_buffer4_send = ServerHooks::getServerHooks()
    15591563                    .getIndex("buffer4_send"));
    15601564
     1565   
     1566    // Check for subnet4_preselect and lease4_prerelease
     1567    EXPECT_NO_THROW(hook_index_subnet4_preselect = ServerHooks::getServerHooks()
     1568                    .getIndex("subnet4_preselect"));
     1569    EXPECT_NO_THROW(hook_index_lease4_prerelease = ServerHooks::getServerHooks()
     1570                    .getIndex("lease4_prerelease"));
     1571   
     1572
    15611573    EXPECT_TRUE(hook_index_buffer4_receive > 0);
    15621574    EXPECT_TRUE(hook_index_pkt4_receive > 0);
    15631575    EXPECT_TRUE(hook_index_select_subnet > 0);
    15641576    EXPECT_TRUE(hook_index_lease4_release > 0);
    15651577    EXPECT_TRUE(hook_index_pkt4_send > 0);
    15661578    EXPECT_TRUE(hook_index_buffer4_send > 0);
     1579
     1580   
     1581    EXPECT_TRUE(hook_index_subnet4_preselect > 0);
     1582    EXPECT_TRUE(hook_index_lease4_prerelease > 0);
     1583   
    15671584}
    15681585
    15691586// This test verifies that the following option structure can be parsed:
    public: 
    18281845        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_select");
    18291846        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_renew");
    18301847        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_release");
     1848 
     1849        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("subnet4_preselect");
     1850        HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("lease4_prerelease");
    18311851
    18321852        delete srv_;
    18331853    }
  • src/bin/dhcp6/Makefile.am

    diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
    index 8f0dd76..9b95f1f 100644
    a b if CONFIG_BACKEND_JSON 
    6565b10_dhcp6_SOURCES += kea_controller.cc
    6666endif
    6767
     68#If the CUSTOM backend is selected, compile withthe custom_controller.cc. As they are only stub implemention so far,
     69#need to add this Wno-unused-parameter CXXFLAGS, since the parameter in init(string filename) is not used.
     70if CONFIG_BACKEND_CUSTOM
     71AM_CXXFLAGS += -Wno-unused-parameter
     72b10_dhcp6_SOURCES += custom_controller.cc
     73endif
     74
    6875nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc
    6976EXTRA_DIST += dhcp6_messages.mes
    7077
  • new file src/bin/dhcp6/custom_controller.cc

    diff --git a/src/bin/dhcp6/custom_controller.cc b/src/bin/dhcp6/custom_controller.cc
    new file mode 100644
    index 0000000..3107564
    - +  
     1/*
     2 * =====================================================================================
     3 *
     4 *       Filename:  custome_controller.cc
     5 *
     6 *    Description:  Controller to config and start the DHCP service.
     7 *
     8 *        Version:  1.0
     9 *        Created:  07/08/2014 03:56:53 PM
     10 *       Revision:  none
     11 *       Compiler:  gcc
     12 *
     13 *         Author:  David Gong (dgong), dgong@benunets.com
     14 *   Organization:  Benu Networks Inc.
     15 *
     16 * =====================================================================================
     17 */
     18
     19#include <config.h>
     20
     21#include <dhcp6/json_config_parser.h>
     22#include <dhcp6/ctrl_dhcp6_srv.h>
     23#include <dhcp6/dhcp6_log.h>
     24#include <exceptions/exceptions.h>
     25
     26namespace isc {
     27namespace dhcp {
     28
     29/// This function initialize the DHCP6 server using configurations from the specified
     30/// filename.
     31void
     32ControlledDhcpv6Srv::init(const std::string& file_name) {
     33    isc_throw(NotImplemented, "ControlledDhcp6Srv::init() for CustomController has not been implemented.");
     34}
     35
     36/// This function cleans up any active connections before closing the DHCP6 server.
     37void ControlledDhcpv6Srv::cleanup() {
     38    isc_throw(NotImplemented, "ControlledDhcp6Srv::cleanup() for CustomController has not been implemented.");
     39}
     40
     41/// This is a logger initialization for CUSTOM controller backend.
     42/// For now, it's just setting log messages to be printed on stdout the same as the JSON /// backend.
     43void Daemon::loggerInit(const char*, bool verbose) {
     44
     45    setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
     46    setenv("B10_LOGGER_ROOT", "kea", 0);
     47    setenv("B10_LOGGER_SEVERITY", (verbose ? "DEBUG":"INFO"), 0);
     48    setenv("B10_LOGGER_DBGLEVEL", "99", 0);
     49    setenv("B10_LOGGER_DESTINATION",  "stdout", 0);
     50    isc::log::initLogger();
     51}
     52
     53};
     54};
  • src/bin/dhcp6/tests/Makefile.am

    diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
    index 301e509..3768461 100644
    a b dhcp6_unittests_SOURCES += ../kea_controller.cc 
    121121dhcp6_unittests_SOURCES += kea_controller_unittest.cc
    122122endif
    123123
     124# As the unittests for CUSTOM Backend have not been implemented yet, the KEA controller and KEA
     125# controller unitests will be used if the user chooses the CUSTOM controller, to avoid compilation errors for now.
     126if CONFIG_BACKEND_CUSTOM
     127dhcp6_unittests_SOURCES += ../kea_controller.cc
     128dhcp6_unittests_SOURCES += kea_controller_unittest.cc
     129endif
     130
    124131nodist_dhcp6_unittests_SOURCES  = ../dhcp6_messages.h ../dhcp6_messages.cc
    125132nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h
    126133
  • src/lib/asiolink/io_address.cc

    diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
    index 6a6ac18..af4e3e2 100644
    a b  
    2626#include <asiolink/io_error.h>
    2727#include <boost/static_assert.hpp>
    2828
     29
    2930using namespace asio;
    3031using asio::ip::udp;
    3132using asio::ip::tcp;
    operator<<(std::ostream& os, const IOAddress& address) { 
    131132    return (os);
    132133}
    133134
     135std::string
     136convertHWAddrToString(const std::vector<uint8_t>& addr) {
     137    stringstream ss;
     138    for(int i = 0; i < addr.size(); i++)
     139    {
     140        if(addr[i] < 16)
     141            ss << 0;
     142        ss << hex << (unsigned int)addr[i];
     143    }
     144    string output = ss.str();
     145    transform(output.begin(), output.end(), output.begin(), ::toupper);
     146    return output;
     147}
     148
     149std::vector<uint8_t>
     150convertStringToHWAddr(std::string &addr) {
     151    std::vector<uint8_t> hwaddr;
     152    if(addr.size() != 12)
     153        isc_throw(BadValue, "The hw address should have 12 hex digits since each byte has a fixed length of 2 digits.");
     154    int len = addr.size() / 2;
     155    char buffer[3];
     156    for(int i = 0; i < len; i++)
     157    {
     158        memset(buffer, 0, 3);
     159        strncpy(buffer, addr.substr(2 * i, 2).c_str(), 2);
     160        uint8_t byte = (uint8_t)strtol(buffer, NULL, 16);
     161        hwaddr.push_back(byte);
     162    }
     163    return hwaddr;
     164}
     165
    134166} // namespace asiolink
    135167} // namespace isc
  • src/lib/asiolink/io_address.h

    diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
    index cb898e3..1017628 100644
    a b  
    2525#include <functional>
    2626#include <string>
    2727#include <vector>
     28#include <algorithm>
    2829
    2930#include <exceptions/exceptions.h>
    3031
    private: 
    250251std::ostream&
    251252operator<<(std::ostream& os, const IOAddress& address);
    252253
     254/// \brief Convert the HW Address into a string foramt.
     255///
     256/// This method convert the HW address into a string format that is different
     257/// from the toText() method. The output will not have the semicolon in them.
     258/// e.g. XXXXXXXXXXXX rather than XX:XX:XX:XX:XX:XX
     259/// \param addr is the HW Address stored in an unsigned char vector, which typically
     260/// has a length of 6.
     261std::string
     262convertHWAddrToString(const std::vector<uint8_t>& addr);
     263
     264/// \brief Conver the HW Address in the string (without the semicolon) backinto
     265/// a unsigned char vector, which can be used directly by the HWAddr() constructor
     266/// to construct a HWAddr Object.
     267/// \param addr here is the HW Addr in String format.
     268std::vector<uint8_t>
     269convertStringToHWAddr(std::string &addr);
     270
    253271} // namespace asiolink
    254272} // namespace isc
    255273#endif // IO_ADDRESS_H
  • src/lib/dhcp/pkt4.cc

    diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
    index 08107a5..4d95c91 100644
    a b Pkt4::pack() { 
    165165
    166166void
    167167Pkt4::unpack() {
     168    unpack(0);
     169}
     170
     171void
     172Pkt4::unpack(int index) {
    168173
     174    if(index < 0 || index >= data_.size())
     175    {
     176        isc_throw(OutOfRange, "Pkt4::unpack(int index), the specified index is out of the range of the received packet!");
     177    }
    169178    // input buffer (used during message reception)
    170     isc::util::InputBuffer buffer_in(&data_[0], data_.size());
     179    isc::util::InputBuffer buffer_in(&data_[index], data_.size() - index);
    171180
    172181    if (buffer_in.getLength() < DHCPV4_PKT_HDR_LEN) {
    173182        isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
    Pkt4::addOption(boost::shared_ptr<Option> opt) { 
    442451    options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
    443452}
    444453
     454std::vector<boost::shared_ptr<isc::dhcp::Option> >
     455Pkt4::getAllOption82(void) {
     456    std::pair< OptionCollection::iterator, OptionCollection::iterator> range;
     457    range = options_.equal_range(DHO_DHCP_AGENT_OPTIONS);
     458    std::vector<OptionPtr> option82;
     459    for(OptionCollection::iterator it = range.first; it != range.second; ++it)
     460    {
     461        option82.push_back(it->second);
     462    }
     463    return option82;
     464}
     465
    445466boost::shared_ptr<isc::dhcp::Option>
    446467Pkt4::getOption(uint8_t type) const {
    447468    OptionCollection::const_iterator x = options_.find(type);
    Pkt4::updateTimestamp() { 
    467488}
    468489
    469490bool
    470 Pkt4::isRelayed() const {
     491Pkt4::isRelayed(bool strict) const {
    471492    static const IOAddress zero_addr("0.0.0.0");
    472493    // For non-relayed message both Giaddr and Hops are zero.
    473494    if (getGiaddr() == zero_addr && getHops() == 0) {
    Pkt4::isRelayed() const { 
    477498    } else if (getGiaddr() != zero_addr && getHops() > 0) {
    478499        return (true);
    479500    }
     501 
     502    //If the server does not check relay strictly, return false even if giaddr is zero while hops is greater than 0.
     503    else if (getGiaddr() == zero_addr && getHops() > 0 && !strict) {
     504            return (false);
     505    }
     506   
    480507    // In any other case, the packet is considered malformed.
    481508    isc_throw(isc::BadValue, "invalid combination of giaddr = "
    482509              << getGiaddr().toText() << " and hops = "
  • src/lib/dhcp/pkt4.h

    diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
    index d5817b8..a8589c0 100644
    a b public: 
    9090    /// Method with throw exception if packet parsing fails.
    9191    void unpack();
    9292
     93    /// @ brief Parses on-wire form of DHCPv4 packet, staring from the index instead of the first byte.
     94    ///
     95    /// Parses received packet, stored in on-wire format in bufferIn_, but from the index byte.
     96    ///
     97    /// This function could replace the unpack() function by setting the default index value to be zero.
     98    ///
     99    /// This method could be useful for the buffer4_recv() callout, so the library developpers can skip
     100    /// some low-layer headers if necessary, to properly unpack the DHCP message.
     101    void unpack(int index);
     102
    93103    /// @brief performs sanity check on a packet.
    94104    ///
    95105    /// This is usually performed after unpack(). It checks if packet is sane:
    public: 
    326336    void
    327337    addOption(boost::shared_ptr<Option> opt);
    328338
     339    /// Return a vector of shared_ptr to option 82s.
     340    /// Some vendors may implement subtypes of Option 82 in seperate Option 82s.
     341    /// Thus we need to return all of them, instead the first found.
     342   
     343    std::vector<boost::shared_ptr<Option> >
     344    getAllOption82(void);
     345   
    329346    /// @brief Returns an option of specified type.
    330347    ///
    331348    /// @return returns option of requested type (or NULL)
    public: 
    502519    /// (true) or non-relayed (false).
    503520    /// @throw isc::BadValue if invalid combination of Giaddr and Hops values is
    504521    /// found.
    505     bool isRelayed() const;
     522    ///
     523    /// A bool parameter strict, which has a default value of true, has been introduced
     524    /// to indicate whether a direct DHCP message that has a HOPS field greater than 
     525    /// zero should throw an exception. Some DHCP Client vendors may set the HOPS
     526    /// field to 1, even if it is a direct DHCP message.
     527    bool isRelayed(bool strict = true) const;
    506528
    507529    /// @brief Set callback function to be used to parse options.
    508530    ///
  • src/lib/dhcp/tests/pkt4_unittest.cc

    diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
    index bfebe77..1f8e475 100644
    a b TEST_F(Pkt4Test, constructor) { 
    303303    );
    304304}
    305305
     306// Check whether pkt4::unpack(int index) works correctly
     307// Note: right now, we cannot insert extra bytes at the beginning of the pkt
     308// so we will only test the range check, and whether it can decode from 0.
     309// when we can insert bytes at the beginning of a pkt, we will further
     310// test whether we can successfully unpack a packet with low-layer headers
     311TEST_F(Pkt4Test, unpackWithIndex) {
     312
     313    vector<uint8_t> expectedFormat = generateTestPacket2();
     314
     315    expectedFormat.push_back(0x63);
     316    expectedFormat.push_back(0x82);
     317    expectedFormat.push_back(0x53);
     318    expectedFormat.push_back(0x63);
     319
     320    expectedFormat.push_back(0x35); // message-type
     321    expectedFormat.push_back(0x1);
     322    expectedFormat.push_back(0x1);
     323
     324    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
     325                                         expectedFormat.size()));;
     326    size_t len = pkt->len();
     327    //Set the index to a negative value or a value greater than the packet size
     328    EXPECT_THROW(pkt->unpack(-5), OutOfRange);
     329    EXPECT_THROW(pkt->unpack(len + 1), OutOfRange);
     330
     331    //Unpack it with index equals 0, which is equivalent to pkt->unpack()
     332    EXPECT_NO_THROW(pkt->unpack(0));
     333}
    306334
    307335TEST_F(Pkt4Test, fixedFields) {
    308336
    TEST_F(Pkt4Test, file) { 
    561589    EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
    562590}
    563591
     592
     593// Test whether the getAllOption82 method behavors correctly
     594TEST_F(Pkt4Test, getAllOption82) {
     595    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPDISCOVER, 0x1234));
     596    vector<boost::shared_ptr<Option> > output;
     597    //There is no option 82 now, so it should return an empty vector
     598    EXPECT_NO_THROW(output = pkt->getAllOption82());
     599    EXPECT_TRUE(output.empty());
     600
     601    vector<uint8_t> payload1(7, 0xaa);
     602    vector<uint8_t> payload2(15, 0xff);
     603
     604    boost::shared_ptr<Option> opt1(new Option(Option::V4, 82, payload1));
     605
     606    pkt->addOption(opt1);
     607    output.clear();
     608    //There is only one option 82 now, so expect a vector with one element
     609    EXPECT_NO_THROW(output = pkt->getAllOption82());
     610    EXPECT_TRUE(output.size() == 1);
     611    //The returned option should be opt1
     612    EXPECT_EQ(output[0], opt1);
     613
     614    ///Note: The addOption function checks whether the option is unique in the packet, so the following test will not succeed.
     615    //Need to disable the check to uncomment the following section
     616    /*
     617    //Add another option 82, so getAllOption82 should return a vector with two elements
     618    boost::shared_ptr<Option> opt2(new Option(Option::V4, 82, payload2));
     619    pkt->addOption(opt2);
     620    output.clear();
     621    EXPECT_NO_THROW(output = pkt->getAllOption82());
     622    EXPECT_TRUE(output.size() == 2);
     623    //The two returned options should be opt1 and opt2
     624    EXPECT_EQ(output[0], opt1);
     625    EXPECT_EQ(output[1], opt2);
     626    */
     627   
     628}
     629
    564630TEST_F(Pkt4Test, options) {
    565631    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 0));
    566632
    TEST_F(Pkt4Test, isRelayed) { 
    821887    EXPECT_THROW(pkt.isRelayed(), isc::BadValue);
    822888}
    823889
     890// This test verifies whether the relaycheckstrict flag is correct.
     891// In particular, when the giaddr is 0 while the hops is greater than 0,
     892// the return value of isRelay is determined by the strict flag
     893TEST_F(Pkt4Test, isRelayed_strict) {
     894    Pkt4 pkt(DHCPDISCOVER, 1234);
     895   
     896    //By default hops = 0 and giaddr = 0, so the return is always false regardless of the strict parameter
     897    EXPECT_FALSE(pkt.isRelayed(true));
     898    EXPECT_FALSE(pkt.isRelayed(false));
     899
     900    //Now set the hops to 1. Now if the strict parameter is true, then throw an exception, otherwise, return false
     901    pkt.setHops(1);
     902    EXPECT_THROW(pkt.isRelayed(true), isc::BadValue);
     903    EXPECT_FALSE(pkt.isRelayed(false));
     904}
     905
     906
    824907// Tests whether a packet can be assigned to a class and later
    825908// checked if it belongs to a given class
    826909TEST_F(Pkt4Test, clientClasses) {
  • src/lib/dhcpsrv/Makefile.am

    diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
    index 6044edc..d8da5d9 100644
    a b AM_CPPFLAGS += $(PGSQL_CPPFLAGS) 
    1313endif
    1414
    1515AM_CXXFLAGS = $(B10_CXXFLAGS)
     16# As some methods in the new In memory database lease manager have not been implemented yet, we enable
     17# this CXXFLAGS to avoid compilation errors. This can be removed after the MEMDB_LEASE_MGR has been fully
     18# implemented.
     19AM_CXXFLAGS += -Wno-unused-parameter
    1620
    1721# Define rule to build logging source files from message file
    1822dhcpsrv_messages.h dhcpsrv_messages.cc: s-messages
    endif 
    6771if HAVE_PGSQL
    6872libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
    6973endif
     74
     75# Choose the proper file to comile with if the with-dhcp-memdb option is specified
     76# during configuration/compilation of the DHCP server.
     77if HAVE_MEMDB
     78libkea_dhcpsrv_la_SOURCES += memdb_lease_mgr.h
     79libkea_dhcpsrv_la_SOURCES += memdb_lease_mgr.cc
     80endif
     81
     82libkea_dhcpsrv_la_SOURCES += custom_allocator.cc
     83
    7084libkea_dhcpsrv_la_SOURCES += option_space_container.h
    7185libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
    7286libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
  • src/lib/dhcpsrv/alloc_engine.cc

    diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
    index 11b0700..7a988e2 100644
    a b  
    1515#include <dhcpsrv/alloc_engine.h>
    1616#include <dhcpsrv/dhcpsrv_log.h>
    1717#include <dhcpsrv/lease_mgr_factory.h>
     18#include <dhcpsrv/memdb_lease_mgr.h>
    1819
    1920#include <hooks/server_hooks.h>
    2021#include <hooks/hooks_manager.h>
    2122
    2223#include <cstring>
    2324#include <vector>
    24 #include <string.h>
     25#include <string>
    2526
    2627using namespace isc::asiolink;
    2728using namespace isc::hooks;
     29using namespace std;
    2830
    2931namespace {
    3032
    AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts, 
    262264    case ALLOC_RANDOM:
    263265        allocators_[basic_type] = AllocatorPtr(new RandomAllocator(basic_type));
    264266        break;
     267    // Introduced a new allocator, where the developpers may customize how to pick
     268    // an address for a client based on management policies.
     269    case ALLOC_CUSTOM:
     270        allocators_[basic_type] = AllocatorPtr(new CustomAllocator(basic_type));
     271        break;
    265272    default:
    266273        isc_throw(BadValue, "Invalid/unsupported allocation algorithm");
    267274    }
    AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid 
    508515        if (!hwaddr) {
    509516            isc_throw(InvalidOperation, "HWAddr must be defined");
    510517        }
    511 
    512518        // Check if there's existing lease for that subnet/clientid/hwaddr combination.
    513         Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
    514         if (existing) {
     519        Lease4Ptr existing;
     520        HWAddrPtr relay_mac;
     521        //TODO: Here we should initialize VLAN to the global default value
     522        uint16_t vlan = 0;
     523
     524        // Check whether the subnet4 is context-based or not. If so, try to retrive the context-based
     525        // lease, otherwise, retrive the lease in the convention manner.
     526        Subnet4ContextPtr subnet4context = boost::dynamic_pointer_cast<Subnet4Context>(subnet);
     527        if(subnet4context)
     528        {
     529            relay_mac = HWAddrPtr(new HWAddr(subnet4context->getRelayMAC()));
     530            vlan = subnet4context->getVLAN();
     531        }
     532       
     533        if(subnet4context)
     534            existing = LeaseMgrFactory::instance().getLease4Context(*hwaddr, subnet->getID(), relay_mac, vlan);
     535        else
     536            existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
     537        // TODO: Do we need to check whether the existing lease is valid in the current subnet?
     538       
     539        if (existing) {
    515540            // Save the old lease, before renewal.
    516541            old_lease.reset(new Lease4(*existing));
    517542            // We have a lease already. This is a returning client, probably after
    AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid 
    528553        }
    529554
    530555        if (clientid) {
    531             existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
     556            //TODO: We need to imlement this if client id would be used by context based lease manager in the future.
     557            existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
    532558            if (existing) {
    533559                // Save the old lease before renewal.
    534560                old_lease.reset(new Lease4(*existing));
    AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid 
    548574
    549575        // check if the hint is in pool and is available
    550576        if (subnet->inPool(Lease::TYPE_V4, hint)) {
    551             existing = LeaseMgrFactory::instance().getLease4(hint);
     577        // If the subnet4 is context-based, try to retrive the context-based
     578        // lease, otherwise, retrive the lease in the convention manner.
     579            if(subnet4context)
     580                existing = LeaseMgrFactory::instance().getLease4Context(hint, relay_mac, vlan);
     581            else
     582                existing = LeaseMgrFactory::instance().getLease4(hint);
    552583            if (!existing) {
    553584                /// @todo: Check if the hint is reserved once we have host support
    554585                /// implemented
    AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid 
    600631
    601632            /// @todo: check if the address is reserved once we have host support
    602633            /// implemented
    603 
    604             Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(candidate);
     634            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, hint.toText().c_str());
     635            Lease4Ptr existing;
     636           
     637            if(candidate.toText() == string("0.0.0.0"))
     638            {
     639                LOG_WARN(dhcpsrv_logger, "Cannot find an unused IPv4 address in the pool. Pool exhausted?");
     640                continue;
     641            }
     642           
     643            // If the subnet4 is context-based, try to retrive the context-based
     644            // lease, otherwise, retrive the lease in the convention manner.
     645            if(subnet4context)
     646                existing = LeaseMgrFactory::instance().getLease4Context(candidate, relay_mac, vlan);
     647            else
     648                existing = LeaseMgrFactory::instance().getLease4(candidate);
    605649            if (!existing) {
    606650                // there's no existing lease for selected candidate, so it is
    607651                // free. Let's allocate it.
    AllocEngine::allocateLease4(const SubnetPtr& subnet, const ClientIdPtr& clientid 
    610654                                               rev_dns_update, hostname,
    611655                                               callout_handle, fake_allocation);
    612656                if (lease) {
     657
    613658                    return (lease);
    614659                }
    615660
    Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet, 
    9831028        isc_throw(BadValue, "Can't create a lease with NULL HW address");
    9841029    }
    9851030    time_t now = time(NULL);
    986 
     1031   
    9871032    // @todo: remove this kludge after ticket #2590 is implemented
    9881033    std::vector<uint8_t> local_copy;
    9891034    if (clientid) {
    Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet, 
    10401085
    10411086    if (!fake_allocation) {
    10421087        // That is a real (REQUEST) allocation
     1088
    10431089        bool status = LeaseMgrFactory::instance().addLease(lease);
    10441090        if (status) {
    10451091            return (lease);
    Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet, 
    10551101
    10561102        // It is for OFFER only. We should not insert the lease into LeaseMgr,
    10571103        // but rather check that we could have inserted it.
    1058         Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(addr);
     1104        Lease4Ptr existing;
     1105
     1106        // If the subnet4 is context-based, try to retrive the context-based
     1107        // lease, otherwise, retrive the lease in the convention manner.
     1108        Subnet4ContextPtr subnet4contextptr = boost::dynamic_pointer_cast<Subnet4Context>(subnet);
     1109        if(subnet4contextptr)
     1110        {
     1111            HWAddrPtr relay_mac = HWAddrPtr(new HWAddr(subnet4contextptr->getRelayMAC()));
     1112            uint16_t vlan = subnet4contextptr->getVLAN();
     1113            existing = LeaseMgrFactory::instance().getLease4Context(addr, relay_mac, vlan);
     1114        }
     1115        else
     1116            existing = LeaseMgrFactory::instance().getLease4(addr);
    10591117        if (!existing) {
    10601118            return (lease);
    10611119        } else {
  • src/lib/dhcpsrv/alloc_engine.h

    diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
    index ed2a767..21545d1 100644
    a b protected: 
    205205                    const isc::asiolink::IOAddress& hint);
    206206    };
    207207
     208    /// @brief Custom allcator that picks address according to management policies.
     209    ///
     210    /// @todo: This is a skeleton class for now and is missing implementation.
     211    class CustomAllocator : public Allocator {
     212    public:
     213
     214        /// @brief default constructor (does nothing)
     215        /// @param type - specifes allocation type
     216        CustomAllocator(Lease::Type type);
     217       
     218        /// @brief returns an address from pool of specified subnet
     219        ///
     220        /// @todo: Implement this method
     221        ///
     222        /// @param subnet an address will be picked from pool of that subnet
     223        /// @param duid Client's DUID (ignored)
     224        /// @param hint the last address that was picked (ignored)
     225        /// @return a random address from the pool
     226        virtual isc::asiolink::IOAddress
     227        pickAddress(const SubnetPtr& subnet, const DuidPtr& duid,
     228                const isc::asiolink::IOAddress& hint);
     229    };
     230
    208231    public:
    209232
    210233    /// @brief specifies allocation type
    211234    typedef enum {
    212235        ALLOC_ITERATIVE, // iterative - one address after another
    213236        ALLOC_HASHED,    // hashed - client's DUID/client-id is hashed
    214         ALLOC_RANDOM     // random - an address is randomly selected
     237        ALLOC_RANDOM,     // random - an address is randomly selected
     238        ALLOC_CUSTOM
    215239    } AllocType;
    216240
    217241
  • src/lib/dhcpsrv/cfgmgr.cc

    diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
    index 12f1e5b..debad37 100644
    a b CfgMgr::getD2ClientMgr() { 
    439439
    440440CfgMgr::CfgMgr()
    441441    : datadir_(DHCP_DATA_DIR),
    442       all_ifaces_active_(false), echo_v4_client_id_(true),
     442      all_ifaces_active_(false), echo_v4_client_id_(true), relay_check_strict_(true),
    443443      d2_client_mgr_() {
    444444    // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
    445445    // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
  • src/lib/dhcpsrv/cfgmgr.h

    diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
    index 3331135..2509db2 100644
    a b public: 
    397397        return (echo_v4_client_id_);
    398398    }
    399399
     400    /// @brief Sets whether server should check the relay information strictly.
     401    ///
     402    /// @param strict should the server check relay strictly or not
     403    void relayCheckStrict(const bool strict) {
     404        relay_check_strict_ = strict;
     405    }
     406
     407    /// @brief Returns whether server checks relay information strictly.
     408    /// @return true if the server checks relay info strictly, false otherwise.
     409    bool relayCheckStrict() const {
     410        return (relay_check_strict_);
     411    }
     412
    400413    /// @brief Updates the DHCP-DDNS client configuration to the given value.
    401414    ///
    402415    /// @param new_config pointer to the new client configuration.
    private: 
    514527    /// Indicates whether v4 server should send back client-id
    515528    bool echo_v4_client_id_;
    516529
     530    /// Indidates whether the server should conduct the relay sanity check strictly. (e.g. giaddr != 0 but hops > 0)
     531    bool relay_check_strict_;
     532
    517533    /// @brief Manages the DHCP-DDNS client and its configuration.
    518534    D2ClientMgr d2_client_mgr_;
    519535};
  • new file src/lib/dhcpsrv/custom_allocator.cc

    diff --git a/src/lib/dhcpsrv/custom_allocator.cc b/src/lib/dhcpsrv/custom_allocator.cc
    new file mode 100644
    index 0000000..4644557
    - +  
     1/*
     2 * =====================================================================================
     3 *
     4 *       Filename:  custom_allocator.cc
     5 *
     6 *    Description:  Custom Lease Allocator.
     7 *
     8 *        Version:  1.0
     9 *        Created:  07/08/2014 08:09:07 PM
     10 *       Revision:  none
     11 *       Compiler:  gcc
     12 *
     13 *         Author:  David Gong (dgong), dgong@benunets.com
     14 *   Organization:  Benu Networks Inc.
     15 *
     16 * =====================================================================================
     17 */
     18
     19//The implemention of custom allocator is placed in a separate file, such that any helper definitions/functions
     20//will not pollute the isc::dhcp namespace.
     21
     22#include <dhcpsrv/alloc_engine.h>
     23#include <dhcpsrv/dhcpsrv_log.h>
     24
     25using namespace isc::asiolink;
     26
     27namespace isc {
     28namespace dhcp {
     29
     30AllocEngine::CustomAllocator::CustomAllocator(Lease::Type lease_type)
     31    :Allocator(lease_type) {
     32}
     33
     34//TODO: This function is yet to be implemented.
     35isc::asiolink::IOAddress
     36AllocEngine::CustomAllocator::pickAddress(const SubnetPtr& subnet,
     37                                        const DuidPtr&,
     38                                        const IOAddress&) {
     39    isc_throw(NotImplemented, "CustomAllocator::pickAddress() has not been implemented.");
     40}
     41}
     42}
  • src/lib/dhcpsrv/dbaccess_parser.cc

    diff --git a/src/lib/dhcpsrv/dbaccess_parser.cc b/src/lib/dhcpsrv/dbaccess_parser.cc
    index 4fdcbfd..a39d16f 100644
    a b DbAccessParser::build(isc::data::ConstElementPtr config_value) { 
    8585    }
    8686
    8787    // b. Check if the 'type; keyword known and throw an exception if not.
     88    // New leasemgr backend memdb has been added.
    8889    string dbtype = type_ptr->second;
    89     if ((dbtype != "memfile") && (dbtype != "mysql") && (dbtype != "postgresql")) {
     90    if ((dbtype != "memfile") && (dbtype != "mysql") && (dbtype != "postgresql") && (dbtype != "memdb")) {
    9091        isc_throw(BadValue, "unknown backend database type: " << dbtype
    9192                  << " (" << config_value->getPosition() << ")");
    9293    }
  • src/lib/dhcpsrv/dhcpsrv_messages.mes

    diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
    index cfcff51..bd1f2dc 100644
    a b lease from the PostgreSQL database for the specified address. 
    483483A debug message issued when the server is attempting to update IPv6
    484484lease from the PostgreSQL database for the specified address.
    485485
     486% DHCPSRV_MEM_DB opening in memory lease database: %1
     487This informational message is logged when a DHCP server (either V4 or
     488V6) is about to open an in memory lease database.  The parameters of the
     489connection including database host, port and timeout needed to access it
     490are logged.
     491
    486492% DHCPSRV_UNEXPECTED_NAME database access parameters passed through '%1', expected 'lease-database'
    487493The parameters for access the lease database were passed to the server through
    488494the named configuration parameter, but the code was expecting them to be
  • src/lib/dhcpsrv/lease.cc

    diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc
    index 7553ab4..6144e10 100644
    a b Lease::hasIdenticalFqdn(const Lease& other) const { 
    6363            fqdn_rev_ == other.fqdn_rev_);
    6464}
    6565
     66Lease4Context::Lease4Context(const Lease4& other)
     67    : Lease4(other) {
     68    // Maybe we can define a constant value for the default vlan.
     69    vlan_ = 1;
     70}
     71
     72Lease4Context::Lease4Context(const Lease4Context& other)
     73    : Lease4(other) {
     74    vlan_ = other.vlan_;
     75    relay_mac_ = other.relay_mac_;
     76    context_fields_ = other.context_fields_;
     77}
     78
     79bool
     80Lease4Context::matches(const Lease4Context &other) const {
     81    if(vlan_ != other.vlan_)
     82        return false;
     83    if(!relay_mac_ || !other.relay_mac_)
     84        return false;
     85    if(relay_mac_ != other.relay_mac_)
     86        return false;
     87    if(context_fields_.size() != other.context_fields_.size())
     88        return false;
     89    map<string, string>::const_iterator it;
     90    for(it = context_fields_.begin(); it != context_fields_.end(); it++)
     91    {
     92        if(other.context_fields_.find(it->first) == other.context_fields_.end() || it->second != other.context_fields_.at(it->first))
     93            return false;
     94    }
     95   
     96    return Lease4::matches(other);
     97}
     98
    6699Lease4::Lease4(const Lease4& other)
    67100    : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
    68101            other.subnet_id_, other.cltt_, other.fqdn_fwd_,
    Lease4::matches(const Lease4& other) const { 
    111144
    112145}
    113146
     147Lease4Context::Lease4Context() : Lease4(){
     148    vlan_ = 1;
     149}
     150
     151Lease4Context&
     152Lease4Context::operator=(const Lease4& other) {
     153    vlan_ = 1;
     154    relay_mac_.reset();
     155    context_fields_.clear();
     156    Lease4::operator=(other);
     157    return (*this);
     158}
     159
     160Lease4Context&
     161Lease4Context::operator=(const Lease4Context& other) {
     162    if(this != &other) {
     163        vlan_ = other.vlan_;
     164        relay_mac_ = other.relay_mac_;
     165        context_fields_.clear();
     166        context_fields_ = other.context_fields_;
     167        Lease4::operator=(other);
     168    }
     169    return (*this);
     170}
     171
    114172Lease4&
    115173Lease4::operator=(const Lease4& other) {
    116174    if (this != &other) {
    Lease6::toText() const { 
    200258}
    201259
    202260std::string
     261Lease4Context::toText() const {
     262    ostringstream stream;
     263
     264    stream << "VLAN:\t" << vlan_ << "\n"
     265           << "Relay MAC:\t" << relay_mac_->toText() << "\n";
     266
     267    map<string, string>::const_iterator it;
     268    for(it = context_fields_.begin(); it != context_fields_.end(); it++)
     269    {
     270        stream << it->first << "\t" << it->second << "\n";
     271    }
     272
     273    stream << Lease4::toText();
     274    return (stream.str());
     275}
     276
     277std::string
    203278Lease4::toText() const {
    204279    ostringstream stream;
    205280
    Lease4::toText() const { 
    213288    return (stream.str());
    214289}
    215290
     291bool
     292Lease4Context::operator==(const Lease4Context & other) const {
     293    if((relay_mac_ && !other.relay_mac_) ||
     294       (!relay_mac_ && other.relay_mac_))
     295        // One lease has relay_mac, but the other doesn't
     296        return false;
     297    if(relay_mac_ && other.relay_mac_ &&
     298                relay_mac_ != other.relay_mac_)
     299        return false;
     300    return (matches(other) && Lease4::operator==(other));
     301}
    216302
    217303bool
    218304Lease4::operator==(const Lease4& other) const {
  • src/lib/dhcpsrv/lease.h

    diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h
    index 90e73d1..d3fe08b 100644
    a b  
    1919#include <dhcp/duid.h>
    2020#include <dhcp/option.h>
    2121#include <dhcp/hwaddr.h>
     22#include <map>
    2223
    2324namespace isc {
    2425namespace dhcp {
    struct Lease4 : public Lease { 
    205206            client_id_.reset(new ClientId(clientid, clientid_len));
    206207        }
    207208    }
     209    Lease4(const isc::asiolink::IOAddress& addr, std::vector<uint8_t> &hwaddr, std::vector<uint8_t> &client_id,
     210            uint32_t valid_lft, uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id, const bool fqdn_fwd = false,
     211            const bool fqdn_rev = false, const std::string& hostname = "")
     212        : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev, hostname),
     213        ext_(0), hwaddr_(hwaddr) {
     214            if(!client_id.empty())
     215                client_id_.reset(new ClientId(client_id));
    208216
     217    }
    209218    /// @brief Default constructor
    210219    ///
    211220    /// Initialize fields that don't have a default constructor.
    typedef boost::shared_ptr<Lease4> Lease4Ptr; 
    273282/// @brief A collection of IPv4 leases.
    274283typedef std::vector<Lease4Ptr> Lease4Collection;
    275284
     285/// @brief Structure that holds a context-based lease for IPv4 address
     286///
     287/// Sometimes it is desirable to allocate lease based on context, not only subnet information.
     288/// Here we define the context based on the VLAN id and the MAC address from which the DHCP
     289/// mesage is relayed.
     290struct Lease4Context : public Lease4 {
     291    /// @brief MAC address of the relay node for the context-based lease
     292    HWAddrPtr relay_mac_;
     293
     294    /// @brief VLAN ID for the context-based lease
     295    uint16_t vlan_;
     296
     297    /// @brief a collection of custom fields for the context-base lease
     298    std::map<std::string, std::string> context_fields_;
     299
     300    /// @brief Default Constructor
     301    Lease4Context();
     302
     303    /// @brief Constructor with parameters.
     304    /// All parameters except the relay_mac, vlan, and context_fields are the same as the Lease4 struct.
     305    Lease4Context(HWAddrPtr &relay_mac, uint16_t vlan, std::map<std::string, std::string> context_fields,
     306            const isc::asiolink::IOAddress& addr, std::vector<uint8_t> &hwaddr, std::vector<uint8_t> &client_id,
     307            uint32_t valid_lft, uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id, const bool fqdn_fwd = false,
     308            const bool fqdn_rev = false, const std::string& hostname = "")
     309        :Lease4(addr, hwaddr, client_id, valid_lft, t1, t2, cltt, subnet_id, fqdn_fwd, fqdn_rev, hostname),
     310         relay_mac_(relay_mac), context_fields_(context_fields){
     311        if(vlan == 0 || vlan > 4096) {
     312            isc_throw(BadValue, "The VLAN ID should be within the range of 1 to 4096");
     313        }
     314        vlan_ = vlan;
     315    }
     316
     317    /// @brief Copy constructor
     318    ///
     319    /// @param other the @c Lease4Context object to be copied.
     320    Lease4Context(const Lease4Context& other);
     321    Lease4Context(const Lease4 &other);
     322    /// @brief Check if two objects encapsulate the lease for the same
     323    /// client.
     324    ///
     325    /// Checks if two @c Lease4Context objects have the relay_mac, vlan id,
     326    /// custom fields, and fields in Lease4.  If these parameters match it is an
     327    /// indication that both objects describe the lease for the same
     328    /// client but apparently one is a result of renewal of the other. The
     329    /// special case of the matching lease is the one that is equal to another.
     330    ///
     331    /// @param other A lease to compare with.
     332    ///
     333    /// @return true if the selected parameters of the two leases match.
     334    bool matches(const Lease4Context& other) const;
     335
     336    /// @brief Assignment operator.
     337    ///
     338    /// @param other the @c Lease4Context object to be assigned.
     339    Lease4Context& operator=(const Lease4Context& other);
     340   
     341    /// @brief Assignment operator from base struct Lease4
     342    /// The additional fields will be set to the default value.
     343    Lease4Context& operator=(const Lease4 & other);
     344
     345    /// @brief Compare two leases for equality
     346    ///
     347    /// @param other lease4Context object with which to compare
     348    bool operator==(const Lease4Context& other) const;
     349
     350    /// @brief Compare two leases for inequality
     351    ///
     352    /// @param other lease4 object with which to compare
     353    bool operator!=(const Lease4Context& other) const {
     354        return (!operator==(other));
     355    }
     356
     357    /// @brief Convert lease to printable form
     358    ///
     359    /// @return Textual represenation of lease data
     360    virtual std::string toText() const;
     361
     362    /// @todo: Add DHCPv4 failover related fields here
     363};
     364
     365/// @brief Pointer to a Lease4 structure.
     366typedef boost::shared_ptr<Lease4Context> Lease4ContextPtr;
     367
     368/// @brief A collection of IPv4 leases.
     369typedef std::vector<Lease4ContextPtr> Lease4ContextCollection;
     370
    276371/// @brief Structure that holds a lease for IPv6 address and/or prefix
    277372///
    278373/// For performance reasons it is a simple structure, not a class. If we chose
  • src/lib/dhcpsrv/lease_mgr.cc

    diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
    index 6a8f358..daca6bb 100644
    a b std::string LeaseMgr::getParameter(const std::string& name) const { 
    4242    return (param->second);
    4343}
    4444
     45Lease4Ptr
     46LeaseMgr::getLease4Context(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan) const {
     47    isc_throw(NotImplemented, "LeaseMgr::getLease4Context() is called, but it is not implemented.");
     48}
     49
     50Lease4Ptr
     51LeaseMgr::getLease4Context(const HWAddr& hwaddr, SubnetID subnet_id, HWAddrPtr &relay_mac, uint16_t vlan) const
     52{
     53    isc_throw(NotImplemented, "LeaseLgr::getLease4Context() is called, but it is not implemented.");
     54}
     55
     56bool
     57LeaseMgr::deleteLeaseContext(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan) {
     58    isc_throw(NotImplemented, "LeaseMgr::deleteLeaseContext() is called, but it is not implemented.");
     59}
     60
    4561Lease6Ptr
    4662LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
    4763                    uint32_t iaid, SubnetID subnet_id) const {
  • src/lib/dhcpsrv/lease_mgr.h

    diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
    index 45ae218..1b7ad22 100644
    a b public: 
    170170    /// @return smart pointer to the lease (or NULL if a lease is not found)
    171171    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const = 0;
    172172
     173    /// @brief Returns an IPv4 lease for specified IPv4 address and context.
     174    ///
     175    /// This method return a lease that is associated with a given address and the context.
     176    /// As the same private IP address may be assigned in different contexts. The IPv4 address
     177    /// itself is insufficient to retrive the lease information if context-based lease
     178    /// allocation is used.
     179    ///
     180    /// @param addr address of the searched lease
     181    /// @param relay_mac MAC address of the relay node
     182    /// @param vlan VLAN id of the client
     183    /// @return smart pointer to the lease (or NULL if a lease is not found)
     184    virtual Lease4Ptr getLease4Context(const isc::asiolink::IOAddress& addr, HWAddrPtr& relay_mac, uint16_t vlan) const;
     185
    173186    /// @brief Returns existing IPv4 leases for specified hardware address.
    174187    ///
    175188    /// Although in the usual case there will be only one lease, for mobile
    public: 
    195208    virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
    196209                                SubnetID subnet_id) const = 0;
    197210
     211    /// @brief Returns an IPv4 lease for specified HWAddr and context.
     212    ///
     213    /// This method return a lease that is associated with a HW Address and the context.
     214    /// Although the HWAddr can usually be used as an unique ID to retrive the lease, but
     215    /// leases may be stored in different tables/documents in context-based lease allocation.
     216    /// Thus it would be much more efficient to retrive the lease based on HWAddr as well
     217    /// as the context information.
     218    ///
     219    /// @param hwaddr MAC address of the searched lease
     220    /// @param relay_mac MAC address of the relay node
     221    /// @param vlan VLAN id of the client
     222    /// @return smart pointer to the lease (or NULL if a lease is not found)
     223    virtual Lease4Ptr getLease4Context(const isc::dhcp::HWAddr& hwaddr, SubnetID subnet_id, HWAddrPtr & relay_mac, uint16_t vlan) const;
     224
    198225    /// @brief Returns existing IPv4 lease for specified client-id
    199226    ///
    200227    /// Although in the usual case there will be only one lease, for mobile
    public: 
    322349    /// @return true if deletion was successful, false if no such lease exists
    323350    virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;
    324351
     352    /// @brief Deletes a context-based lease.
     353    ///
     354    /// @param addr Address of the lease to be deleted. (This can be IPv4 or
     355    ///        IPv6.)
     356    ///
     357    //  @param relay_mac and vlan determines the context of the lease.
     358    ///
     359    /// @return true if deletion was successful, false if no such lease exists
     360    virtual bool deleteLeaseContext(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan);
     361
    325362    /// @brief Return backend type
    326363    ///
    327364    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
  • src/lib/dhcpsrv/lease_mgr_factory.cc

    diff --git a/src/lib/dhcpsrv/lease_mgr_factory.cc b/src/lib/dhcpsrv/lease_mgr_factory.cc
    index 9fd6789..f6ea949 100644
    a b  
    2323#ifdef HAVE_PGSQL
    2424#include <dhcpsrv/pgsql_lease_mgr.h>
    2525#endif
     26#ifdef HAVE_MEMDB
     27#include <dhcpsrv/memdb_lease_mgr.h>
     28#endif
    2629
    2730#include <boost/algorithm/string.hpp>
    2831#include <boost/foreach.hpp>
    LeaseMgrFactory::create(const std::string& dbaccess) { 
    135138        return;
    136139    }
    137140#endif
     141#ifdef HAVE_MEMDB
     142    // If the new MEMDB lease manager is configured, the lease manager
     143    // factory should create an instance of the memdbLeaseMgr.
     144    if (parameters[type] == string("memdb")) {
     145        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEM_DB).arg(redacted);
     146        getLeaseMgrPtr().reset(new MemDBLeaseMgr(parameters));
     147        return;
     148    }
     149#endif
    138150    if (parameters[type] == string("memfile")) {
    139151        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
    140152        getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));
  • new file src/lib/dhcpsrv/memdb_lease_mgr.cc

    diff --git a/src/lib/dhcpsrv/memdb_lease_mgr.cc b/src/lib/dhcpsrv/memdb_lease_mgr.cc
    new file mode 100644
    index 0000000..394b941
    - +  
     1/*
     2 * =====================================================================================
     3 *
     4 *       Filename:  memdb_lease_mgr.cc
     5 *
     6 *    Description:  MEMDB Backend for Lease Manager.
     7 *
     8 *        Version:  1.0
     9 *        Created:  07/08/2014 05:35:48 PM
     10 *       Revision:  none
     11 *       Compiler:  gcc
     12 *
     13 *         Author:  David Gong (dgong), dgong@benunets.com
     14 *   Organization:  Benu Networks Inc.
     15 *
     16 * =====================================================================================
     17 */
     18
     19#include <config.h>
     20
     21#include <dhcpsrv/dhcpsrv_log.h>
     22#include <dhcpsrv/memdb_lease_mgr.h>
     23#include <boost/static_assert.hpp>
     24
     25using namespace isc;
     26using namespace isc::dhcp;
     27
     28namespace isc {
     29namespace dhcp {
     30
     31MemDBLeaseMgr::MemDBLeaseMgr(const LeaseMgr::ParameterMap& parameters)
     32    : LeaseMgr(parameters)
     33{
     34    openDatabase();
     35}
     36
     37MemDBLeaseMgr::~MemDBLeaseMgr()
     38{
     39}
     40
     41void
     42MemDBLeaseMgr::openDatabase() {
     43    isc_throw(NotImplemented, "MemDBLeaseMgr::openDatabase() was called, but it is not implemented.");
     44}
     45
     46bool
     47MemDBLeaseMgr::addLease(const Lease4Ptr& lease) {
     48    isc_throw(NotImplemented, "MemDBLeaseMgr::addLease() was called, but it is not implemented.");
     49}
     50
     51bool
     52MemDBLeaseMgr::addLease(const Lease6Ptr& lease) {
     53    isc_throw(NotImplemented, "MemDBLeaseMgr::addLease() was called, but it is not implemented.");
     54}
     55
     56std::vector<uint32_t>
     57MemDBLeaseMgr::getActiveIPv4() {
     58    isc_throw(NotImplemented, "MemDBLeaseMgr::getActiveIPv4() was called, but it is not implemented.");
     59}
     60
     61std::vector<uint32_t>
     62MemDBLeaseMgr::getActiveIPv4Context(isc::dhcp::HWAddr& relay_mac, uint16_t vlan) {
     63    isc_throw(NotImplemented, "MemDBLeaseMgr::getActiveIPv4Context() was called, but it is not implemented.");
     64}
     65
     66Lease4Ptr MemDBLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
     67    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     68}
     69
     70Lease4Ptr MemDBLeaseMgr::getLease4Context(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan) const {
     71    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4Context() was called, but it is not implemented.");
     72}
     73
     74Lease4Collection MemDBLeaseMgr::getLease4(const isc::dhcp::HWAddr& hwaddr) const {
     75    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     76}
     77
     78Lease4Ptr MemDBLeaseMgr::getLease4(const isc::dhcp::HWAddr& hwaddr, SubnetID subnet_id) const {
     79    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     80}
     81
     82Lease4Ptr MemDBLeaseMgr::getLease4Context(const isc::dhcp::HWAddr& hwaddr, SubnetID subnet_id, HWAddrPtr &relay_mac, uint16_t vlan) const {
     83    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4Context() was called, but it is not implemented.");
     84}
     85Lease4Collection MemDBLeaseMgr::getLease4(const ClientId& clientid) const {
     86    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     87}
     88
     89Lease4Ptr MemDBLeaseMgr::getLease4(const ClientId& client_id, const HWAddr& hwaddr, SubnetID subnet_id) const {
     90    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     91}
     92
     93Lease4Ptr MemDBLeaseMgr::getLease4(const ClientId& client_id, SubnetID subnet_id) const {
     94    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease4() was called, but it is not implemented.");
     95}
     96
     97Lease6Ptr MemDBLeaseMgr::getLease6(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
     98    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease6() was called, but it is not implemented.");
     99}
     100
     101Lease6Collection MemDBLeaseMgr::getLeases6(Lease::Type type, const DUID& duid, uint32_t aid) const {
     102    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease6() was called, but it is not implemented.");
     103}
     104
     105Lease6Collection MemDBLeaseMgr::getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const {
     106    isc_throw(NotImplemented, "MemDBLeaseMgr::getLease6() was called, but it is not implemented.");
     107}
     108
     109void MemDBLeaseMgr::updateLease4(const Lease4Ptr& lease4) {
     110    isc_throw(NotImplemented, "MemDBLeaseMgr::updateLease4() was called, but it is not implemented.");
     111}
     112
     113void MemDBLeaseMgr::updateLease6(const Lease6Ptr &lease6) {
     114    isc_throw(NotImplemented, "MemDBLeaseMgr::updateLease6() was called, but it is not implemented.");
     115}
     116
     117bool MemDBLeaseMgr::deleteLeaseContext(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan) {
     118    isc_throw(NotImplemented, "MemDBLeaseMgr::deleteLeaseContext() was called, but it is not implemented.");
     119}
     120bool MemDBLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     121    isc_throw(NotImplemented, "MemDBLeaseMgr::deleteLease() was called, but it is not implemented.");
     122}
     123
     124std::string
     125MemDBLeaseMgr::getName() const {
     126    std::string name = "";
     127    try {
     128        name = getParameter("name");
     129    }
     130    catch (...) {
     131        // Return an empty name
     132    }
     133    return name;
     134}
     135
     136std::string
     137MemDBLeaseMgr::getDescription() const {
     138    return (std::string("In Memory Database"));
     139}
     140
     141std::pair<uint32_t, uint32_t>
     142MemDBLeaseMgr::getVersion() const {
     143    uint32_t major;
     144    uint32_t minor;
     145    major = 0;
     146    minor = 1;
     147
     148    return (std::make_pair(major, minor));
     149}
     150
     151void
     152MemDBLeaseMgr::commit() {
     153    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, "Commit: no-op for Redis.");
     154    return;
     155}
     156
     157void
     158MemDBLeaseMgr::rollback() {
     159    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, "Rollback: no-op for Redis.");
     160}
     161
     162}
     163}
  • new file src/lib/dhcpsrv/memdb_lease_mgr.h

    diff --git a/src/lib/dhcpsrv/memdb_lease_mgr.h b/src/lib/dhcpsrv/memdb_lease_mgr.h
    new file mode 100644
    index 0000000..34e6784
    - +  
     1/*
     2 * =====================================================================================
     3 *
     4 *       Filename:  memdb_lease_mgr.h
     5 *
     6 *    Description:  In Memory Database Backend for BIND DHCP Lease Manager
     7 *
     8 *        Version:  1.0
     9 *        Created:  06/11/2014 09:30:44 AM
     10 *       Revision:  none
     11 *       Compiler:  gcc
     12 *
     13 *         Author:  David Gong (dgong), dgong@benunets.com
     14 *   Organization:  Benu Networks Inc.
     15 *
     16 * =====================================================================================
     17 */
     18
     19#ifndef MEMDB_LEASE_MGR_H
     20#define MEMDB_LEASE_MGR_H
     21
     22#include <dhcp/hwaddr.h>
     23#include <dhcpsrv/lease_mgr.h>
     24
     25#include <boost/scoped_ptr.hpp>
     26#include <boost/utility.hpp>
     27
     28namespace isc {
     29namespace dhcp {
     30
     31///@ brief Class definition for the MemDBLeaseMgr.
     32///
     33/// This new lease manager would have basically the same interface as other lease managers, such as mysql, pgsql,
     34/// But the backend in-memory database will be used to further improve the performance.
     35class MemDBLeaseMgr : public LeaseMgr {
     36public:
     37    MemDBLeaseMgr(const ParameterMap & parameters);
     38    virtual ~MemDBLeaseMgr();
     39    virtual bool addLease(const Lease4Ptr& lease);
     40    virtual bool addLease(const Lease6Ptr& lease);
     41    virtual std::vector<uint32_t> getActiveIPv4(void);
     42    virtual std::vector<uint32_t> getActiveIPv4Context(isc::dhcp::HWAddr &relay_mac, uint16_t vlan);
     43    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
     44    virtual Lease4Ptr getLease4Context(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan) const;
     45    virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
     46    virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr, SubnetID subnet_id) const;
     47    virtual Lease4Ptr getLease4Context(const isc::dhcp::HWAddr& hwaddr, SubnetID subnet_id, HWAddrPtr &relay_mac, uint16_t vlan) const;
     48    virtual Lease4Collection getLease4(const ClientId& clientid) const;
     49    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr, SubnetID subnet_id) const;
     50    virtual Lease4Ptr getLease4(const ClientId& client_id, SubnetID subnet_id) const;
     51    virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress& addr) const;
     52    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, uint32_t aid) const;
     53    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
     54    virtual void updateLease4(const Lease4Ptr &lease4);
     55    virtual void updateLease6(const Lease6Ptr &lease6);
     56    virtual bool deleteLeaseContext(const isc::asiolink::IOAddress& addr, HWAddrPtr &relay_mac, uint16_t vlan);
     57    virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
     58    virtual std::string getType() const {
     59        return (std::string("MemDB"));
     60    }
     61    virtual std::string getName() const;
     62    virtual std::string getDescription() const;
     63    virtual std::pair<uint32_t, uint32_t> getVersion() const;
     64    virtual void commit();
     65    virtual void rollback();
     66private:
     67    void openDatabase();
     68    bool deleteLease(const std::string &, const std::string &);
     69};
     70
     71};  // end of isc::dhcp namespace
     72};  // end of isc namespace
     73
     74#endif //MEMDB_LEASE_MGR_H
  • src/lib/dhcpsrv/pool.cc

    diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
    index d9c3da0..4c58728 100644
    a b Pool::toText() const { 
    3939    return (tmp.str());
    4040}
    4141
     42isc::asiolink::IOAddress
     43Pool::nextAddress(const isc::asiolink::IOAddress& current)
     44{
     45    const std::vector<uint8_t>& vec = current.toBytes();
     46    const int len = vec.size();
     47
     48    if(len != 4)
     49        isc_throw(BadValue, "Pool4::nextAddress() only takes IPv4 address!");
     50
     51    uint8_t packed[4];
     52    std::memcpy(packed, &vec[0], 4);
     53   
     54    for(int i = len - 1; i >= 0; --i)
     55    {
     56        ++packed[i];
     57        if(packed[i] != 0)
     58            break;
     59    }
     60   
     61    return (isc::asiolink::IOAddress::fromBytes(current.getFamily(), packed));
     62}
     63
     64Pool4Custom::Pool4Custom(const isc::asiolink::IOAddress& first,
     65        const isc::asiolink::IOAddress& last)
     66:Pool4(first, last) {
     67}
     68
     69Pool4Custom::Pool4Custom(const isc::asiolink::IOAddress& prefix,
     70    uint8_t prefix_len)
     71:Pool4(prefix, prefix_len)
     72{
     73    uint32_t firstaddr = first_;
     74    uint32_t lastaddr  = last_;
     75    firstaddr += 2;
     76    lastaddr -= 2;
     77    if(firstaddr > lastaddr)
     78        isc_throw(BadValue, "The pool is too small to allocate IPv4 address (0x00 for network id, 0xff for broadcast, 0x01 and 0xfe for potential gateway)");
     79    first_ = firstaddr;
     80    last_  = lastaddr;
     81}
     82
     83Pool4Custom::Pool4Custom(const isc::asiolink::IOAddress& first_last,
     84        const isc::asiolink::IOAddress& prefix,
     85        uint8_t prefix_len, bool is_first)
     86:Pool4(prefix, prefix_len)
     87{
     88    uint32_t firstaddr;
     89    uint32_t lastaddr;
     90    if(is_first)
     91    {
     92        first_ = first_last;
     93        firstaddr = first_;
     94        lastaddr = last_;
     95        lastaddr -= 2;
     96    }
     97    else
     98    {
     99        last_ = first_last;
     100        firstaddr = first_;
     101        firstaddr += 2;
     102        lastaddr = last_;
     103    }
     104
     105    if(firstaddr > lastaddr)
     106        isc_throw(BadValue, "The pool is too small to allocate IPv4 address (0x00 for network id, 0xff for broadcast, 0x01 and 0xfe for potential gateway)");
     107    if(is_first)
     108        last_ = lastaddr;
     109    else
     110        first_ = firstaddr;
     111}
     112
    42113Pool4::Pool4(const isc::asiolink::IOAddress& first,
    43114             const isc::asiolink::IOAddress& last)
    44115:Pool(Lease::TYPE_V4, first, last) {
    Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len) 
    65136        isc_throw(BadValue, "Invalid prefix length");
    66137    }
    67138
     139    // Benu: The first address should also be updated based on the prefix
     140    first_ = firstAddrInPrefix(prefix, prefix_len);
     141
    68142    // Let's now calculate the last address in defined pool
    69143    last_ = lastAddrInPrefix(prefix, prefix_len);
    70144}
  • src/lib/dhcpsrv/pool.h

    diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h
    index ae8cb58..3ab36d3 100644
    a b public: 
    5555        return (last_);
    5656    }
    5757
     58    /// @brief Returns the address next to the current address.
     59    /// This function could be handy to other Alloc Engines. So
     60    /// we copy it from the iterative allocator to here.
     61    isc::asiolink::IOAddress
     62    nextAddress(const isc::asiolink::IOAddress & current);
    5863    /// @brief Checks if a given address is in the range.
    5964    ///
    6065    /// @return true, if the address is in pool
    public: 
    139144    ///
    140145    /// @param prefix specifies prefix of the pool
    141146    /// @param prefix_len specifies length of the prefix of the pool
     147    /// Note we need to get the network id from the prefix, instead of using it directly
     148    /// as first address. This should also applies to the Pool4 class!
    142149    Pool4(const isc::asiolink::IOAddress& prefix,
    143150          uint8_t prefix_len);
    144151};
    public: 
    146153/// @brief a pointer an IPv4 Pool
    147154typedef boost::shared_ptr<Pool4> Pool4Ptr;
    148155
     156/// @brief Define a customized Pool4 class to choose proper first and
     157/// last addresses, if they are not explicitly specified.
     158///
     159/// The objective of this class is to avoid allocating the network
     160/// address, broadcast address, and gateway addresses.
     161class Pool4Custom : public Pool4 {
     162public:
     163    /// @brief the constructor for Pool4Custom "min-max" style definition
     164    ///
     165    /// @param first is the first address in a pool
     166    /// @param last is the last address in a pool
     167    Pool4Custom(const isc::asiolink::IOAddress& first,
     168            const isc::asiolink::IOAddress& last);
     169
     170    /// @brief the constructor for Pool4Custom "prefix/len" style definition
     171    ///
     172    /// @param prefix specifies prefix of the pool
     173    /// @param prefix_len specifies length of the prefix
     174    Pool4Custom(const isc::asiolink::IOAddress& prefix,
     175            uint8_t prefix_len);
     176
     177    /// @brief the constructor for Pool4Custom "first/last, prefix, len" style definition
     178    ///
     179    /// @param first_last is the first or the last address in the pool
     180    /// @param prefix is the prefix of the pool
     181    /// @param len is the length of the prefix
     182    /// @param is_first indicates whether the first_last parameter is the first or last address of the pool.
     183    Pool4Custom(const isc::asiolink::IOAddress& first_last,
     184            const isc::asiolink::IOAddress& prefix,
     185            uint8_t prefix_len, bool is_first);
     186};
     187
     188typedef boost::shared_ptr<Pool4Custom> Pool4CustomPtr;
     189
    149190/// @brief Pool information for IPv6 addresses and prefixes
    150191///
    151192/// It holds information about pool6, i.e. a range of IPv6 address space that
  • src/lib/dhcpsrv/subnet.cc

    diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
    index ceba9af..3303772 100644
    a b isc::asiolink::IOAddress Subnet4::getSiaddr() const { 
    234234    return (siaddr_);
    235235}
    236236
     237Subnet4Context::Subnet4Context(const isc::asiolink::IOAddress& prefix, uint8_t length,
     238        const Triplet<uint32_t>& t1,
     239        const Triplet<uint32_t>& t2,
     240        const Triplet<uint32_t>& valid_lifetime,
     241        const HWAddr &relay_mac, uint16_t vlan, const SubnetID id)
     242:Subnet4(prefix, length, t1, t2, valid_lifetime, id), relay_mac_(relay_mac)
     243{
     244    setVLAN(vlan);
     245}
     246
     247void Subnet4Context::setVLAN(const uint16_t vlan) {
     248    // The VLAN ID should be between 1 and 4096.
     249    if(vlan == 0 || vlan > 4096)
     250        isc_throw(BadValue, "The VLAN ID should be between 1 and 4096.");
     251    vlan_ = vlan;
     252}
     253
     254void Subnet4Context::setContextField(const std::string& key, const std::string& value) {
     255    if(key.empty() || key == "")
     256        isc_throw(BadValue, "The key of the context field cannot be empty.");
     257    context_fields_[key] = value;
     258}
     259
     260std::string Subnet4Context::getContextField(const std::string &key) const{
     261    if(key.empty() || context_fields_.find(key) == context_fields_.end())
     262        isc_throw(BadValue, "There is no such field in the Subnet4Context definition.");
     263
     264    return context_fields_.at(key);
     265}
     266
     267int Subnet4Context::getNumberOfContextFields() const {
     268    return context_fields_.size();
     269}
     270
    237271const PoolCollection& Subnet::getPools(Lease::Type type) const {
    238272    // check if the type is valid (and throw if it isn't)
    239273    checkType(type);
  • src/lib/dhcpsrv/subnet.h

    diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
    index 35be8fa..97ae2c4 100644
    a b  
    3131#include <dhcpsrv/triplet.h>
    3232#include <dhcpsrv/lease.h>
    3333
     34#include <map>
     35
    3436namespace isc {
    3537namespace dhcp {
    3638
    typedef boost::shared_ptr<Subnet4> Subnet4Ptr; 
    693695/// @brief A collection of Subnet6 objects
    694696typedef std::vector<Subnet4Ptr> Subnet4Collection;
    695697
     698/// @brief This class represents a Context-based Subnet4.
     699///
     700/// In some scenarios, it is desirable to further classify DHCP messages from the same relay server, or
     701/// coming from the same interface (Direct DHCP). For example, all clients connected to the same switch
     702/// may belong to different VLANs but have the same relay address. It would be a useful feature to be able
     703/// to map VLANs to different subnets. Moreover, the aforementioned switch could be an aggregation switch,
     704/// on which each port is connected to an access switch. Then it might be required to map a different subnet
     705/// to each access switch (port on the aggregation switch), even though some of them are in the same VLAN.
     706/// Thus, we extend the definition of subnet4 into subnet4context, to include the relay MAC address, as well
     707/// as the VLAN ID. In a subnet with context, it would be useful to be able to define customized fields, which
     708///could be used for lease allocation, or dynamic DNS, etc.
     709class Subnet4Context : public Subnet4 {
     710public:
     711    /// @brief Constructor with all parameters.
     712    /// @param prefix, length, t1, t2, valid_lifetime are the same as Subnet4 class
     713    /// @param relay_mac MAC address of the relay node for the context-based subnet
     714    /// @param VLAN ID of the context-based subnet
     715    /// @param SubnetID the same as Subnet4
     716    Subnet4Context(const isc::asiolink::IOAddress& prefix, uint8_t length,
     717            const Triplet<uint32_t>& t1,
     718            const Triplet<uint32_t>& t2,
     719            const Triplet<uint32_t>& valid_lifetime,
     720            const HWAddr & relay_mac, uint16_t vlan, const SubnetID id = 0);
     721   
     722    /// @brief Sets the Relay MAC for Subnet4Context
     723    void setRelayMAC(const isc::dhcp::HWAddr & mac) {
     724        relay_mac_ = mac;
     725    }
     726
     727    /// @brief Returns the Relay MAC for Subnet4Context
     728    /// @return relay MAC value
     729    isc::dhcp::HWAddr getRelayMAC() const {
     730        return relay_mac_;
     731    }
     732
     733    /// @brief Sets the VLAN ID for Subnet4Context
     734    void setVLAN(const uint16_t vlan);
     735
     736    /// @brief Returns the VLAN ID for Subnet4Context
     737    /// @return VLAN ID
     738    uint16_t getVLAN() const {
     739        return vlan_;
     740    }
     741
     742    /// @brief Sets a field in the Subnet4Context
     743    /// @param key name of the context field
     744    /// @param value value of the context field
     745    void setContextField(const std::string& key, const std::string &value);
     746
     747    /// @brief Returns the value of a field in the Subnet4Context
     748    /// @param key name of the context field
     749    /// @return value of the context field
     750    std::string getContextField(const std::string& key) const;
     751
     752    /// @brief Returns the number of context fields
     753    /// @return number of context fields
     754    int getNumberOfContextFields() const;
     755
     756protected:
     757    /// @brief MAC address of the relay node for context-based Subnet
     758    HWAddr   relay_mac_;
     759
     760    /// @brief VLAN ID of the relay node for context-based Subnet
     761    uint16_t vlan_;
     762
     763    /// @brief container for the custom fields of the context
     764    std::map<std::string, std::string> context_fields_;
     765
     766};
     767
     768typedef boost::shared_ptr<Subnet4Context> Subnet4ContextPtr;
    696769
    697770/// @brief A configuration holder for IPv6 subnet.
    698771///
  • src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
    index 37f8e8f..e5c00f4 100644
    a b TEST_F(CfgMgrTest, activateAllIfaces) { 
    10521052    EXPECT_FALSE(cfg_mgr.isActiveIface("eth2"));
    10531053}
    10541054
     1055// This test verifies that the RelayCheckStrict option can be configured.
     1056// if relaycheckstrict is true, a dhcp message with giaddr = 0 while hops > 0
     1057// will be dropped; otherwise, it will be processed.
     1058TEST_F(CfgMgrTest, relayCheckStrict) {
     1059    CfgMgr& cfg_mgr = CfgMgr::instance();
     1060
     1061    //Check that the default is true
     1062    EXPECT_TRUE(cfg_mgr.relayCheckStrict());
     1063
     1064    //Check that it can be modified to false
     1065    cfg_mgr.relayCheckStrict(false);
     1066    EXPECT_FALSE(cfg_mgr.relayCheckStrict());
     1067
     1068    //Check that the default value can be restored
     1069    cfg_mgr.relayCheckStrict(true);
     1070    EXPECT_TRUE(cfg_mgr.relayCheckStrict());
     1071}
     1072
    10551073// This test verifies that RFC6842 (echo client-id) compatibility may be
    10561074// configured.
    10571075TEST_F(CfgMgrTest, echoClientId) {
  • src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
    index 4925e3c..3774793 100644
    a b GenericLeaseMgrTest::testLease4NullClientId() { 
    501501}
    502502
    503503void
     504GenericLeaseMgrTest::testGetLease4Context() {
     505    IOAddress addr(straddress4_[0]);
     506    HWAddr hwaddr(vector<uint8_t>(6, 0xff), HTYPE_ETHER);
     507    HWAddrPtr relay_mac;
     508    uint16_t vlan = 100;
     509    uint32_t subnet_id = 5;
     510
     511    //Expect NotImplemented as neither getLease4Context() methods has been implemented
     512    EXPECT_THROW(lmptr_->getLease4Context(addr, relay_mac, vlan), NotImplemented);
     513    EXPECT_THROW(lmptr_->getLease4Context(hwaddr, subnet_id, relay_mac, vlan), NotImplemented);
     514}
     515
     516void
     517GenericLeaseMgrTest::testDeleteLeaseContext() {
     518    IOAddress addr(straddress4_[1]);
     519    HWAddrPtr relay_mac;
     520    uint16_t vlan = 100;
     521
     522    //Expect NotImplemented exception as deleteLease4Context() has not been implemented in most Lease Mgrs.
     523    EXPECT_THROW(lmptr_->deleteLeaseContext(addr, relay_mac, vlan), NotImplemented);
     524}
     525
     526void
    504527GenericLeaseMgrTest::testGetLease4HWAddr1() {
    505528    // Let's initialize two different leases 4 and just add the first ...
    506529    Lease4Ptr leaseA = initializeLease4(straddress4_[5]);
  • src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h

    diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
    index 68e656d..8d53b47 100644
    a b public: 
    123123    /// HWAddr information.
    124124    void testGetLease4HWAddr2();
    125125
     126    /// @brief Check getLease4Context methods - access by IOAddress or HWAddr
     127    ///
     128    /// Since the GetLease4Context methods have not been implemented in most LeaseMgrs (mysql, pgsql, memfile)
     129    /// We should expect a NotImplemented exception when running this test
     130    void testGetLease4Context();
     131
     132    /// @brief Check deleteLeaseContext method - access by IOAddress
     133    ///
     134    /// Similar to getLease4Context() methods, this method has not been implemented in most LeaseMgrs
     135    /// So expect a NotImplemented exception when running this test
     136    void testDeleteLeaseContext();
     137
    126138    /// @brief Test lease retrieval using client id, HW address and subnet id.
    127139    void testGetLease4ClientIdHWAddrSubnetId();
    128140
  • src/lib/dhcpsrv/tests/lease_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc
    index 4e7451f..b74b6eb 100644
    a b  
    1515#include <config.h>
    1616#include <asiolink/io_address.h>
    1717#include <dhcp/duid.h>
     18#include <dhcp/hwaddr.h>
    1819#include <dhcpsrv/lease.h>
    1920#include <gtest/gtest.h>
    2021#include <vector>
    Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd, 
    5253    return (lease);
    5354}
    5455
     56// Returns a Lease4Context instance
     57Lease4Context createLease4Context() {
     58    Lease4Context lease4c;
     59    lease4c.vlan_ = 200;
     60    lease4c.relay_mac_ = HWAddrPtr(new HWAddr(std::vector<uint8_t>(6, 0x33), HTYPE_ETHER));
     61    lease4c.addr_ = IOAddress("192.168.1.255");
     62    lease4c.context_fields_["Key1"] = "Value1";
     63    lease4c.hwaddr_ = std::vector<uint8_t>(6, 0x22);
     64    lease4c.cltt_ = time(NULL);
     65    lease4c.fqdn_fwd_ = false;
     66    lease4c.fqdn_rev_ = false;
     67    lease4c.valid_lft_ = 1800;
     68    lease4c.subnet_id_ = 80;
     69    lease4c.t1_ = 0;
     70    lease4c.t2_ = 0;
     71    lease4c.hostname_ = "test.example.com";
     72   
     73    return lease4c;
     74}
     75
     76/// This test checks if the Lease4Context structure can be instantiated correctly.
     77/// We will mainly focus on the new fields introduced in Lease4Context, since
     78/// it calls the constructor of Lease4 to initialize existing fields in Lease4.
     79TEST(Lease4Context, constructor) {
     80    //Construct a Lease4Context object using the parameter-less constructor
     81    Lease4Context lease4c1;
     82    //The VLAN ID should be equal to the default vlan id: 1
     83    EXPECT_EQ(lease4c1.vlan_, 1);
     84    //The relay_mac should be a null smart pointer
     85    EXPECT_FALSE(lease4c1.relay_mac_);
     86    //The context_fields should be empty
     87    EXPECT_TRUE(lease4c1.context_fields_.empty());
     88   
     89    //Construct a Lease4Context object using the second constructor
     90    //Simple HW Address
     91    std::vector<uint8_t> hwaddr(6, 0x2b);
     92    //Simple Client ID
     93    std::vector<uint8_t> clientid(7, 0xff);
     94    //last transaction time and lease lifetime
     95    time_t current = time(NULL);
     96    uint32_t lifetime = 3600;
     97    // The uint32 converter of the IOAddress class will return the uint32_t version of an IPv4 address
     98    uint32_t address = IOAddress("192.168.1.1");
     99    // Subnet ID
     100    uint32_t subnet_id = 50;
     101
     102    // Relay MAC, VLAN ID and context_fields for the Lease4Context struct
     103    HWAddrPtr relay_mac = HWAddrPtr(new HWAddr(std::vector<uint8_t>(6, 0x3c), HTYPE_ETHER));
     104    uint16_t vlan = 100;
     105    std::map<std::string, std::string> context_fields;
     106    context_fields["Key1"] = "Value1";
     107   
     108    Lease4Context lease4c2(relay_mac, vlan, context_fields, address, hwaddr, clientid, lifetime, 0, 0, current, subnet_id, false, false, "");
     109
     110    //Check the relay_mac, vlan and context_fields
     111    EXPECT_EQ(lease4c2.relay_mac_, relay_mac);
     112    EXPECT_EQ(lease4c2.vlan_, 100);
     113    //The context_field map should be the same.
     114    EXPECT_EQ(lease4c2.context_fields_, context_fields);
     115
     116    //In the constructor, we check the range of the vlan ID to be within 1- 4096.
     117    //But we cannot guarantee this restriction, since as a public member of the struct,
     118    //any function can modify it directly. But the VLAN information will mostly copied
     119    //from the subnet4Context class, where the VLAN range restriction is enforced.
     120   
     121    vlan = 4097;
     122    EXPECT_THROW(Lease4Context lease4c3(relay_mac, vlan, context_fields, address, hwaddr, clientid, lifetime, 0, 0, current, subnet_id), BadValue);
     123
     124}
     125
     126// This test verfies that the copy constructors of Lease4Context are implemented correctly
     127TEST(Lease4Context, copyConstructor) {
     128   //First test the copy constructor from Lease4Context to Lease4Context
     129   Lease4Context lease4c1 = createLease4Context();
     130   Lease4Context lease4c2(lease4c1);
     131
     132   //We assume that the equal operator works correctly.
     133   EXPECT_EQ(lease4c1, lease4c2);
     134
     135   //Test the copy constructor from Lease4 to Lease4Context
     136   Lease4 lease4 = createLease4("test.example.com", false, false);
     137   Lease4Context lease4c3(lease4);
     138 
     139   //The default VLAN ID should be 1
     140   EXPECT_EQ(lease4c3.vlan_, 1);
     141   //The relay_mac_ should be an empty smart pointer to HWAddr()
     142   EXPECT_FALSE(lease4c3.relay_mac_);
     143   //The context_fields_ should be empty
     144   EXPECT_TRUE(lease4c3.context_fields_.empty());
     145   
     146   //If we slice the derived struct back to Lease4, they should be still the same
     147   Lease4 lease4bak = lease4c3;
     148   //The copy constructor from Lease4 to Lease4Context should leave the Lease4 part intact
     149   EXPECT_EQ(lease4bak, lease4);
     150}
     151
     152// This test verifies that the assignment operators of Lease4Context are implemented correctly
     153TEST(Lease4Context, assignmentOperator) {
     154    //First test the assignment operator from Lease4Context struct
     155    Lease4Context lease4c1 = createLease4Context();
     156    Lease4Context lease4c2 = lease4c1;
     157
     158    //We assume that the equal operator works correctly.
     159    EXPECT_EQ(lease4c1, lease4c2);
     160
     161    //Test the assignment operator from Lease4 to Lease4Context
     162    Lease4 lease41 = createLease4("test.examle.org", true, true);
     163    Lease4Context lease4c3 = lease41;
     164
     165    //The default VLAN ID should be 1
     166    EXPECT_EQ(lease4c3.vlan_, 1);
     167    //The relay_mac_ should be an empty smart pointer
     168    EXPECT_FALSE(lease4c3.relay_mac_);
     169    //The context_fields_ should be empty
     170    EXPECT_TRUE(lease4c3.context_fields_.empty());
     171
     172    //Slice the derived struct back to Lease4, to validate the assignment operator
     173    Lease4 lease42 = lease4c3;
     174    //The Lease4 fields should be intact
     175    EXPECT_EQ(lease42, lease41);
     176}
     177
     178// This test checks the match function of Lease4Context
     179TEST(Lease4Context, matches) {
     180    Lease4Context lease4c1 = createLease4Context();
     181    Lease4Context lease4c2 = lease4c1;
     182
     183    //Matches should return true since they are identical
     184    EXPECT_TRUE(lease4c1.matches(lease4c2));
     185
     186    lease4c1.hostname_ = "test.example.edu";
     187    //shoudl still matches since the match function does not compare fields defined in Lease struct
     188    EXPECT_TRUE(lease4c1.matches(lease4c2));
     189
     190    lease4c1.addr_ = IOAddress("192.168.1.254");
     191    //Should no longer match since we have changed the addr_ field in Lease4
     192    EXPECT_FALSE(lease4c1.matches(lease4c2));
     193
     194    //Change the address back
     195    lease4c1 = lease4c2;
     196    //Verify they matches now
     197    EXPECT_TRUE(lease4c1.matches(lease4c2));
     198   
     199    //Change the vlan ID
     200    lease4c1.vlan_ = 300;
     201    //expect false
     202    EXPECT_FALSE(lease4c1.matches(lease4c2));
     203
     204    //Reset the above change and changes the relay_mac;
     205    lease4c1.vlan_ = 200;
     206    EXPECT_TRUE(lease4c1.matches(lease4c2));
     207    lease4c1.relay_mac_.reset();
     208    EXPECT_FALSE(lease4c1.matches(lease4c2));
     209
     210    //Sets the relay_mac back and change the context_field
     211   
     212    lease4c1.relay_mac_ = lease4c2.relay_mac_;
     213    EXPECT_TRUE(lease4c1.matches(lease4c2));
     214    lease4c1.context_fields_.clear();
     215    EXPECT_FALSE(lease4c1.matches(lease4c2));
     216
     217}
     218
     219/// @brief Lease4Context Equality Check
     220//  Each equalition (operator == ) check is followed by an inequality check (operator !=)
     221TEST(Lease4Context, operatorEqual) {
     222    //We should mainly focus on Lease4Context fields, since the operator== of Lease4() is called to compare other fields
     223    Lease4Context lease4c1 = createLease4Context();
     224    Lease4Context lease4c2 = lease4c1;
     225
     226    //Changing any field in the Lease4Context will result in false for the operator ==
     227    lease4c1.context_fields_.clear();
     228    EXPECT_FALSE(lease4c1 == lease4c2);
     229    EXPECT_TRUE(lease4c1 != lease4c2);
     230
     231    lease4c1.context_fields_ = lease4c2.context_fields_;
     232    EXPECT_TRUE(lease4c1 == lease4c2);
     233    //Even if only one field changes, the == operator should return false;
     234    lease4c1.context_fields_["Key1"] = lease4c1.context_fields_["Key1"] + "!!";
     235    EXPECT_FALSE(lease4c1 == lease4c2);
     236    EXPECT_TRUE(lease4c1 != lease4c2);
     237   
     238    lease4c1.context_fields_ = lease4c2.context_fields_;
     239    EXPECT_TRUE(lease4c1 == lease4c2);
     240    lease4c1.vlan_ = 300;
     241    EXPECT_FALSE(lease4c1 == lease4c2);
     242    EXPECT_TRUE(lease4c1 != lease4c2);
     243   
     244    lease4c1.vlan_ = lease4c2.vlan_;
     245    EXPECT_TRUE(lease4c1 == lease4c2);
     246    lease4c1.relay_mac_.reset();
     247    EXPECT_FALSE(lease4c1 == lease4c2);
     248    EXPECT_TRUE(lease4c1 != lease4c2);
     249
     250    //Leave the detailed tests on fields in Lease4 to the unittest of Lease4
     251    lease4c1.relay_mac_ = lease4c2.relay_mac_;
     252    EXPECT_TRUE(lease4c1 == lease4c2);
     253    lease4c1.addr_ = IOAddress("127.0.0.1");
     254    EXPECT_FALSE(lease4c1 == lease4c2);
     255    EXPECT_TRUE(lease4c1 != lease4c2);
     256
     257    //Leave the detailed tests on fields in Lease to the unittest of Lease
     258    lease4c1.addr_ = lease4c2.addr_;
     259    EXPECT_TRUE(lease4c1 == lease4c2);
     260    lease4c1.fqdn_fwd_ = !lease4c2.fqdn_fwd_;
     261    EXPECT_FALSE(lease4c1 == lease4c2);
     262    EXPECT_TRUE(lease4c1 != lease4c2);
     263
     264}
     265
    55266/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
    56267// This test checks if the Lease4 structure can be instantiated correctly
    57268TEST(Lease4, constructor) {
  • src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
    index d4875be..78e1486 100644
    a b TEST_F(MySqlLeaseMgrTest, getLease4HWAddr2) { 
    355355    testGetLease4HWAddr2();
    356356}
    357357
     358/// @brief Check GetLease4Context methods - access by both IOAddress and HWAddr
     359TEST_F(MysqlLeaseMgrTest, getLease4Context) {
     360    testGetLease4Context();
     361}
     362
     363/// @brief Check DeleteLeaseContext method
     364TEST_F(MysqlLeaseMgrTest, deleteLeaseContext) {
     365    testDeleteLeaseContext();
     366}
     367
    358368// @brief Get lease4 by hardware address (2)
    359369//
    360370// Check that the system can cope with getting a hardware address of
  • src/lib/dhcpsrv/tests/pool_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc
    index 402ca2a..001ccc8 100644
    a b using namespace isc::asiolink; 
    3030
    3131namespace {
    3232
     33
    3334TEST(Pool4Test, constructor_first_last) {
    3435
    3536    // let's construct 192.0.2.1-192.0.2.255 pool
    TEST(Pool4Test, constructor_first_last) { 
    5051                       IOAddress("192.0.2.1")), BadValue);
    5152}
    5253
     54
    5355TEST(Pool4Test, constructor_prefix_len) {
    5456
    5557    // let's construct 2001:db8:1::/96 pool
    TEST(Pool4Test, unique_id) { 
    102104}
    103105
    104106// Simple check if toText returns reasonable values
    105 TEST(Poo4Test,toText) {
     107TEST(Pool4Test,toText) {
    106108    Pool4 pool1(IOAddress("192.0.2.7"), IOAddress("192.0.2.17"));
    107109    EXPECT_EQ("type=V4, 192.0.2.7-192.0.2.17", pool1.toText());
    108110
    TEST(Poo4Test,toText) { 
    110112    EXPECT_EQ("type=V4, 192.0.2.128-192.0.2.143", pool2.toText());
    111113}
    112114
     115TEST(Pool4CustomTest, constructorFirstLast) {
     116    //Consturct a 192.168.1.1 - 192.168.1.255 pool
     117    //As the first and last addresses are explicitly specified, the constructor will assume all of them are available
     118    Pool4Custom pool1(IOAddress("192.168.1.1"), IOAddress("192.168.1.255"));
     119
     120    EXPECT_EQ(IOAddress("192.168.1.1"), pool1.getFirstAddress());
     121    EXPECT_EQ(IOAddress("192.168.1.255"), pool1.getLastAddress());
     122
     123    //The last address should be greater than the first address.
     124    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.20"), IOAddress("192.168.1.1")), BadValue);
     125}
     126
     127TEST(Pool4CustomTest, constructorPrefixLen) {
     128    //Construct a 192.168.1.0/24 pool
     129    Pool4Custom pool1(IOAddress("192.168.1.0"), 24);
     130
     131    //The first address in the pool should be 192.168.1.2 (192.168.1.0 is network id, 192.168.1.1 could be gateway)
     132    EXPECT_EQ(IOAddress("192.168.1.2"), pool1.getFirstAddress());
     133    //The last address should be 192.168.1.253 (192.168.1.255 is broadcast address, 192.168.1.254 could be gateway)
     134    EXPECT_EQ(IOAddress("192.168.1.253"), pool1.getLastAddress());
     135
     136    //Test whether the host id would be masked out by the prefix length
     137    Pool4Custom pool2(IOAddress("192.168.1.50"), 24);
     138
     139    //The first address should still be 192.168.1.2, regardless of the "50" host id
     140    EXPECT_EQ(IOAddress("192.168.1.2"), pool2.getFirstAddress());
     141    //The last address should still be 192.168.1.253
     142    EXPECT_EQ(IOAddress("192.168.1.253"), pool2.getLastAddress());
     143
     144    //If the prefix length is 30 or greater, the constructor should throw an exception, since there would be no address in the pool to allocate.
     145    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), 30), BadValue);
     146    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), 31), BadValue);
     147    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), 32), BadValue);
     148
     149}
     150
     151//Test the third constructor for Pool4Custom, only the first or last address is specified, while the prefix and prefix length are given.
     152TEST(Pool4CustomTest, constructorFirst_lastPrefixLen) {
     153    //Construct a 192.168.1.0/24; first = 192.168.1.1 pool
     154    Pool4Custom pool1(IOAddress("192.168.1.1"), IOAddress("192.168.1.0"), 24, true);
     155
     156    //The first address should be 192.168.1.1, as specified.
     157    EXPECT_EQ(IOAddress("192.168.1.1"), pool1.getFirstAddress());
     158    //The last address should be 192.168.1.253 (skip 192.168.1.255 and 192.168.1.254)
     159    EXPECT_EQ(IOAddress("192.168.1.253"), pool1.getLastAddress());
     160
     161    //Construct a 192.168.1.0/24; last = 192.168.1.254 pool
     162    Pool4Custom pool2(IOAddress("192.168.1.254"), IOAddress("192.168.1.0"), 24, false);
     163
     164    // The first address should be 192.168.1.2 (skip 192.168.1.0 and 192.168.1.1)
     165    EXPECT_EQ(IOAddress("192.168.1.2"), pool2.getFirstAddress());
     166    // The last address should be 192.168.1.254, as specified
     167    EXPECT_EQ(IOAddress("192.168.1.254"), pool2.getLastAddress());
     168
     169    //Test small subnet
     170    Pool4Custom pool3(IOAddress("192.168.1.1"), IOAddress("192.168.1.0"), 30, true);
     171    //There would be only one available address in the pool: 192.168.1.1
     172    EXPECT_EQ(IOAddress("192.168.1.1"), pool3.getFirstAddress());
     173    EXPECT_EQ(IOAddress("192.168.1.1"), pool3.getLastAddress());
     174
     175    //The last address is specified
     176    Pool4Custom pool4(IOAddress("192.168.1.2"), IOAddress("192.168.1.0"), 30, false);
     177    //There should be only one available address: 192.168.1.2
     178    EXPECT_EQ(IOAddress("192.168.1.2"), pool4.getFirstAddress());
     179    EXPECT_EQ(IOAddress("192.168.1.2"), pool4.getLastAddress());
     180
     181    //If the first address exceeds the derived last address, throw an exception; vice versa.
     182    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.254"), IOAddress("192.168.1.0"), 24, true), BadValue);
     183    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.1"), IOAddress("192.168.1.0"), 24, false), BadValue);
     184
     185    //Prefix 31 or 32 will definitely throw an exception since there is no available address.
     186    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), IOAddress("192.168.1.0"), 31, true), BadValue);
     187    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.1"), IOAddress("192.168.1.0"), 31, false), BadValue);
     188    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), IOAddress("192.168.1.0"), 32, true), BadValue);
     189    EXPECT_THROW(Pool4Custom(IOAddress("192.168.1.0"), IOAddress("192.168.1.0"), 32, false), BadValue);
     190
     191}
     192
     193TEST(Pool4CustomTest, inRange)
     194{
     195    //First last constructor
     196    Pool4Custom pool1(IOAddress("192.168.1.1"), IOAddress("192.168.1.255"));
     197    EXPECT_FALSE(pool1.inRange(IOAddress("192.168.1.0")));
     198    EXPECT_FALSE(pool1.inRange(IOAddress("192.168.2.1")));
     199    EXPECT_TRUE(pool1.inRange(IOAddress("192.168.1.5")));
     200
     201    //Prefix len constructor
     202    Pool4Custom pool2(IOAddress("192.168.1.0"), 24);
     203    EXPECT_FALSE(pool2.inRange(IOAddress("192.168.1.1")));
     204    EXPECT_FALSE(pool2.inRange(IOAddress("192.168.1.254")));
     205    EXPECT_TRUE(pool2.inRange(IOAddress("192.168.1.2")));
     206    EXPECT_TRUE(pool2.inRange(IOAddress("192.168.1.253")));
     207
     208    //first, prefix, len
     209    Pool4Custom pool3(IOAddress("192.168.1.1"), IOAddress("192.168.1.0"), 24, true);
     210    EXPECT_TRUE(pool3.inRange(IOAddress("192.168.1.1")));
     211    EXPECT_FALSE(pool3.inRange(IOAddress("192.168.1.254")));
     212
     213    //last, prefix, len
     214    Pool4Custom pool4(IOAddress("192.168.1.254"), IOAddress("192.168.1.0"), 24, false);
     215    EXPECT_TRUE(pool4.inRange(IOAddress("192.168.1.254")));
     216    EXPECT_FALSE(pool4.inRange(IOAddress("192.168.1.1")));
     217}
     218
     219
    113220TEST(Pool6Test, constructor_first_last) {
    114221
    115222    // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
  • src/lib/dhcpsrv/tests/subnet_unittest.cc

    diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
    index 99332e7..40c1c71 100644
    a b TEST(Subnet4Test, lastAllocated) { 
    329329    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_PD, addr), BadValue);
    330330}
    331331
     332// Checks the Subnet4Context Constructor
     333TEST(Subnet4ContextTest, constructor) {
     334    // Construct a Subnet4Context object
     335    EXPECT_NO_THROW(Subnet4Context subnet1(IOAddress("10.0.0.0"), 16, 1, 2, 3, HWAddr(isc::dhcp::HWAddr::fromText("00:01:02:03:04:05")), 1, 10));
     336
     337    Subnet4Context subnet1(IOAddress("10.0.0.0"), 16, 1, 2, 3, HWAddr(isc::dhcp::HWAddr::fromText("00:01:02:03:04:05")), 1, 10);
     338    // Check whether the VLAN  ID is set correctly by the constructor
     339    EXPECT_EQ(subnet1.getVLAN(), 1);
     340
     341    // Check whether the Relay MAC is set properly by the constructor
     342    EXPECT_EQ(subnet1.getRelayMAC(), isc::dhcp::HWAddr::fromText("00:01:02:03:04:05"));
     343
     344    // Check whether the number of context fields is zero after construction
     345    EXPECT_EQ(subnet1.getNumberOfContextFields(), 0);
     346
     347    // Use an empty HWAddr
     348    EXPECT_NO_THROW(Subnet4Context subnet2(IOAddress("10.0.1.0"), 16, 1, 2, 3, HWAddr(), 5, 10));
     349
     350    // Use default SubnetID
     351    EXPECT_NO_THROW(Subnet4Context subnet3(IOAddress("10.0.2.0"), 16, 1, 2, 3, HWAddr(), 6));
     352   
     353    // invalid vlan ID (1 - 4096)
     354    EXPECT_THROW(Subnet4Context subnet4(IOAddress("10.0.3.0"), 16, 1, 2, 3, HWAddr(), 8000, 10), BadValue);
     355}
     356
     357// Checks whether we can add and access pool4 or pool4custom in Subnet4 or Subnet4Context
     358TEST(Subnet4ContextTest, pool4Custom) {
     359    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.168.0.0"), 22, 1, 2, 3));
     360    Subnet4ContextPtr subnet2(new Subnet4Context(IOAddress("192.168.1.0"), 22, 1, 2, 3, HWAddr(isc::dhcp::HWAddr::fromText("00:01:02:03:04:05")), 100));
     361
     362    Pool4Ptr pool1(new Pool4(IOAddress("192.168.0.0"), 24));
     363    Pool4Ptr pool2(new Pool4Custom(IOAddress("192.168.1.0"), 24));
     364    Pool4Ptr pool3(new Pool4Custom(IOAddress("192.168.2.1"), IOAddress("192.168.2.253")));
     365    Pool4Ptr pool4(new Pool4Custom(IOAddress("192.168.3.1"), IOAddress("192.168.3.0"), 24, true));
     366
     367    //Add the above defined pools to subnet1
     368    EXPECT_NO_THROW(subnet1->addPool(pool1));
     369    EXPECT_NO_THROW(subnet1->addPool(pool2));
     370    EXPECT_NO_THROW(subnet1->addPool(pool3));
     371    EXPECT_NO_THROW(subnet1->addPool(pool4));
     372
     373    //Add the above defined pools to subnet2
     374    EXPECT_NO_THROW(subnet2->addPool(pool1));
     375    EXPECT_NO_THROW(subnet2->addPool(pool2));
     376    EXPECT_NO_THROW(subnet2->addPool(pool3));
     377    EXPECT_NO_THROW(subnet2->addPool(pool4));
     378
     379    //Get the first pool from both subnets, which should be pool1.
     380    EXPECT_EQ(subnet1->getAnyPool(Lease::TYPE_V4), pool1);
     381    EXPECT_EQ(subnet2->getAnyPool(Lease::TYPE_V4), pool1);
     382
     383    //Get a pool with hint from Subnet4
     384    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.0.10")), pool1);
     385    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.1.20")), pool2);
     386    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.2.30")), pool3);
     387    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.3.40")), pool4);
     388
     389    //Get a pool with hint from Subnet4Context
     390    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.0.10")), pool1);
     391    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.1.20")), pool2);
     392    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.2.30")), pool3);
     393    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.3.40")), pool4);
     394
     395    //Try to get a pool with a hint that is invalid, will return the first pool
     396    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.4.5")), pool1);
     397    //Pool4Custom will not include broadcast and gateway addresses in the pool if the prefix/len format is used.
     398    EXPECT_EQ(subnet1->getPool(Lease::TYPE_V4, IOAddress("192.168.1.1")), pool1);
     399    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.4.5")), pool1);
     400    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.2.254")), pool1);
     401    EXPECT_EQ(subnet2->getPool(Lease::TYPE_V4, IOAddress("192.168.3.254")), pool1);
     402}
     403
     404// Check the set/getVLAN() member function of Subnet4Context
     405TEST(Subnet4ContextTest, VLANCheck)
     406{
     407    Subnet4Context subnet1(IOAddress("10.0.0.0"), 16, 1, 2, 3, HWAddr(), 1, 10);
     408
     409    //Set the VLAN ID
     410    EXPECT_NO_THROW(subnet1.setVLAN(99));
     411
     412    //Get the VLAN ID
     413    EXPECT_EQ(subnet1.getVLAN(), 99);
     414
     415    //Set an invalid VLAN ID
     416    EXPECT_THROW(subnet1.setVLAN(4097), BadValue);
     417}
     418
     419// Check the set/getRelayMAC() member function of Subnet4Context
     420TEST(Subnet4ContextTest, relayMACCheck)
     421{
     422    Subnet4Context subnet1(IOAddress("10.0.0.0"), 16, 1, 2, 3, HWAddr(), 1, 10);
     423   
     424    //Set an empty Relay MAC
     425    EXPECT_NO_THROW(subnet1.setRelayMAC(HWAddr()));
     426
     427    //Get the Relay MAC
     428    EXPECT_EQ(subnet1.getRelayMAC(), HWAddr());
     429
     430    //Set the Relay MAC from a ':' separated string
     431    EXPECT_NO_THROW(subnet1.setRelayMAC(isc::dhcp::HWAddr::fromText("AA:BB:CC:DD:EE:FF")));
     432
     433    //Get the Relay MAC
     434    //Note the toText() function returns a lowercase string, so we need to compare with a lower case version of the MAC address.
     435    EXPECT_EQ(subnet1.getRelayMAC().toText(false), "aa:bb:cc:dd:ee:ff");
     436
     437    //Set the Relay MAC address from a char string
     438    std::vector<uint8_t> mac(6, 'a');
     439    EXPECT_NO_THROW(subnet1.setRelayMAC(isc::dhcp::HWAddr(mac, HTYPE_ETHER)));
     440
     441    //Get the Relay MAC address
     442    EXPECT_EQ(subnet1.getRelayMAC(), isc::dhcp::HWAddr(mac, HTYPE_ETHER));
     443}
     444
     445// Check the set/getContextField() member function of SubnetContext
     446TEST(Subnet4ContextTest, contextFieldCheck)
     447{
     448    Subnet4Context subnet1(IOAddress("10.0.0.0"), 16, 1, 2, 3, HWAddr(), 1, 10);
     449
     450    //Add an empty field
     451    EXPECT_THROW(subnet1.setContextField(std::string(), "dummy"), BadValue);
     452
     453    //Add a field with contents
     454    EXPECT_NO_THROW(subnet1.setContextField("key1", "value1"));
     455
     456    //Add a field without contents
     457    EXPECT_NO_THROW(subnet1.setContextField("key2", ""));
     458
     459    //Get the number of context fields
     460    EXPECT_EQ(subnet1.getNumberOfContextFields(), 2);
     461
     462    //Get the value of field "key1"
     463    EXPECT_EQ(subnet1.getContextField("key1"), std::string("value1"));
     464
     465    //Get the value of field "key2"
     466    EXPECT_EQ(subnet1.getContextField("key2"), std::string());
     467
     468    //Get a value from a field that does not exist
     469    EXPECT_THROW(subnet1.getContextField("key3"), BadValue);
     470}
     471
    332472// Checks if the V4 is the only allowed type for Pool4 and if getPool()
    333473// is working properly.
    334474TEST(Subnet4Test, PoolType) {