dhcp4_srv.cc 74.1 KB
Newer Older
1
// Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
#include <asiolink/io_address.h>
16
#include <dhcp/dhcp4.h>
17
18
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
19
#include <dhcp/iface_mgr.h>
20
#include <dhcp/option4_addrlst.h>
21
#include <dhcp/option_int.h>
22
#include <dhcp/option_int_array.h>
23
#include <dhcp/option_vendor.h>
24
#include <dhcp/option_string.h>
25
#include <dhcp/pkt4.h>
26
#include <dhcp/docsis3_option_defs.h>
27
28
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
29
30
#include <dhcpsrv/addr_utilities.h>
#include <dhcpsrv/callout_handle_store.h>
31
32
33
34
35
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/utils.h>
36
#include <dhcpsrv/utils.h>
37
#include <hooks/callout_handle.h>
38
#include <hooks/hooks_manager.h>
39
#include <util/strutil.h>
40

41
#include <boost/bind.hpp>
42
#include <boost/foreach.hpp>
43
44
45

#include <iomanip>

46
47
using namespace isc;
using namespace isc::asiolink;
48
using namespace isc::dhcp;
49
using namespace isc::dhcp_ddns;
50
using namespace isc::hooks;
51
52
using namespace isc::log;
using namespace std;
53

54
/// Structure that holds registered hook indexes
55
56
struct Dhcp4Hooks {
    int hook_index_buffer4_receive_;///< index for "buffer4_receive" hook point
57
58
    int hook_index_pkt4_receive_;   ///< index for "pkt4_receive" hook point
    int hook_index_subnet4_select_; ///< index for "subnet4_select" hook point
59
    int hook_index_lease4_release_; ///< index for "lease4_release" hook point
60
    int hook_index_pkt4_send_;      ///< index for "pkt4_send" hook point
61
    int hook_index_buffer4_send_;   ///< index for "buffer4_send" hook point
62

63
64
65
    /// Constructor that registers hook points for DHCPv4 engine
    Dhcp4Hooks() {
        hook_index_buffer4_receive_= HooksManager::registerHook("buffer4_receive");
66
67
68
        hook_index_pkt4_receive_   = HooksManager::registerHook("pkt4_receive");
        hook_index_subnet4_select_ = HooksManager::registerHook("subnet4_select");
        hook_index_pkt4_send_      = HooksManager::registerHook("pkt4_send");
69
70
        hook_index_lease4_release_ = HooksManager::registerHook("lease4_release");
        hook_index_buffer4_send_   = HooksManager::registerHook("buffer4_send");
71
72
73
74
75
76
77
    }
};

// Declare a Hooks object. As this is outside any function or method, it
// will be instantiated (and the constructor run) when the module is loaded.
// As a result, the hook indexes will be defined before any method in this
// module is called.
78
Dhcp4Hooks Hooks;
79

80
81
82
namespace isc {
namespace dhcp {

83
84
const std::string Dhcpv4Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");

85
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
86
                     const bool direct_response_desired)
87
: shutdown_(true), alloc_engine_(), port_(port),
88
    use_bcast_(use_bcast), hook_index_pkt4_receive_(-1),
89
90
    hook_index_subnet4_select_(-1), hook_index_pkt4_send_(-1) {

91
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
92
    try {
93
94
95
96
        // Open sockets only if port is non-zero. Port 0 is used for testing
        // purposes in two cases:
        // - when non-socket related testing is performed
        // - when the particular test supplies its own packet filtering class.
97
        if (port) {
98
99
100
101
102
103
104
105
106
            // First call to instance() will create IfaceMgr (it's a singleton)
            // it may throw something if things go wrong.
            // The 'true' value of the call to setMatchingPacketFilter imposes
            // that IfaceMgr will try to use the mechanism to respond directly
            // to the client which doesn't have address assigned. This capability
            // may be lacking on some OSes, so there is no guarantee that server
            // will be able to respond directly.
            IfaceMgr::instance().setMatchingPacketFilter(direct_response_desired);

107
108
109
110
111
112
            // Create error handler. This handler will be called every time
            // the socket opening operation fails. We use this handler to
            // log a warning.
            isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
                boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
            IfaceMgr::instance().openSockets4(port_, use_bcast_, error_handler);
113
        }
114

115
116
117
118
119
120
121
        // Instantiate LeaseMgr
        LeaseMgrFactory::create(dbconfig);
        LOG_INFO(dhcp4_logger, DHCP4_DB_BACKEND_STARTED)
            .arg(LeaseMgrFactory::instance().getType())
            .arg(LeaseMgrFactory::instance().getName());

        // Instantiate allocation engine
122
123
        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100,
                                            false /* false = IPv4 */));
124

125
126
127
128
129
130
131
        // Register hook points
        hook_index_pkt4_receive_   = Hooks.hook_index_pkt4_receive_;
        hook_index_subnet4_select_ = Hooks.hook_index_subnet4_select_;
        hook_index_pkt4_send_      = Hooks.hook_index_pkt4_send_;

        /// @todo call loadLibraries() when handling configuration changes

132
    } catch (const std::exception &e) {
133
        LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
134
135
136
        shutdown_ = true;
        return;
    }
137

Tomek Mrugalski's avatar
Tomek Mrugalski committed
138
    shutdown_ = false;
139
140
141
}

Dhcpv4Srv::~Dhcpv4Srv() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
142
    IfaceMgr::instance().closeSockets();
143
144
}

145
146
void
Dhcpv4Srv::shutdown() {
147
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
148
149
150
    shutdown_ = true;
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
151
152
Pkt4Ptr
Dhcpv4Srv::receivePacket(int timeout) {
153
154
155
    return (IfaceMgr::instance().receive4(timeout));
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
156
157
void
Dhcpv4Srv::sendPacket(const Pkt4Ptr& packet) {
158
159
160
    IfaceMgr::instance().send(packet);
}

161
162
bool
Dhcpv4Srv::run() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
163
    while (!shutdown_) {
164
        /// @todo: calculate actual timeout once we have lease database
165
166
        //cppcheck-suppress variableScope This is temporary anyway
        const int timeout = 1000;
167

Tomek Mrugalski's avatar
Tomek Mrugalski committed
168
        // client's message and server's response
169
        Pkt4Ptr query;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
170
        Pkt4Ptr rsp;
171

172
        try {
173
            query = receivePacket(timeout);
174
175
176
177
        } catch (const std::exception& e) {
            LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
        }

178
179
180
181
182
183
        // Timeout may be reached or signal received, which breaks select()
        // with no reception ocurred
        if (!query) {
            continue;
        }

184
185
186
187
188
189
190
191
192
        // In order to parse the DHCP options, the server needs to use some
        // configuration information such as: existing option spaces, option
        // definitions etc. This is the kind of information which is not
        // available in the libdhcp, so we need to supply our own implementation
        // of the option parsing function here, which would rely on the
        // configuration data.
        query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
                                       _1, _2, _3));

193
194
195
        bool skip_unpack = false;

        // The packet has just been received so contains the uninterpreted wire
196
        // data; execute callouts registered for buffer4_receive.
197
        if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_receive_)) {
198
199
200
201
202
203
204
205
206
            CalloutHandlePtr callout_handle = getCalloutHandle(query);

            // Delete previously set arguments
            callout_handle->deleteAllArguments();

            // Pass incoming packet as argument
            callout_handle->setArgument("query4", query);

            // Call callouts
Tomek Mrugalski's avatar
Tomek Mrugalski committed
207
208
            HooksManager::callCallouts(Hooks.hook_index_buffer4_receive_,
                                       *callout_handle);
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

            // Callouts decided to skip the next processing step. The next
            // processing step would to parse the packet, so skip at this
            // stage means that callouts did the parsing already, so server
            // should skip parsing.
            if (callout_handle->getSkip()) {
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_BUFFER_RCVD_SKIP);
                skip_unpack = true;
            }

            callout_handle->getArgument("query4", query);
        }

        // Unpack the packet information unless the buffer4_receive callouts
        // indicated they did it
        if (!skip_unpack) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
225
226
227
            try {
                query->unpack();
            } catch (const std::exception& e) {
228
229
230
                // Failed to parse the packet.
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
                          DHCP4_PACKET_PARSE_FAIL).arg(e.what());
231
232
                continue;
            }
233
        }
234

Tomek Mrugalski's avatar
Tomek Mrugalski committed
235
236
237
        // Assign this packet to one or more classes if needed. We need to do
        // this before calling accept(), because getSubnet4() may need client
        // class information.
238
239
        classifyPacket(query);

240
241
242
        // Check whether the message should be further processed or discarded.
        // There is no need to log anything here. This function logs by itself.
        if (!accept(query)) {
243
            continue;
244
        }
245

246
        // We have sanity checked (in accept() that the Message Type option
247
248
        // exists, so we can safely get it here.
        int type = query->getType();
249
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
250
            .arg(serverReceivedPacketName(type))
251
            .arg(type)
252
253
            .arg(query->getIface());
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
254
            .arg(type)
255
256
            .arg(query->toText());

Tomek Mrugalski's avatar
Tomek Mrugalski committed
257
        // Let's execute all callouts registered for pkt4_receive
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
        if (HooksManager::calloutsPresent(hook_index_pkt4_receive_)) {
            CalloutHandlePtr callout_handle = getCalloutHandle(query);

            // Delete previously set arguments
            callout_handle->deleteAllArguments();

            // Pass incoming packet as argument
            callout_handle->setArgument("query4", query);

            // Call callouts
            HooksManager::callCallouts(hook_index_pkt4_receive_,
                                       *callout_handle);

            // Callouts decided to skip the next processing step. The next
            // processing step would to process the packet, so skip at this
            // stage means drop.
            if (callout_handle->getSkip()) {
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_RCVD_SKIP);
                continue;
            }
278

279
280
            callout_handle->getArgument("query4", query);
        }
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
        try {
            switch (query->getType()) {
            case DHCPDISCOVER:
                rsp = processDiscover(query);
                break;

            case DHCPREQUEST:
                // Note that REQUEST is used for many things in DHCPv4: for
                // requesting new leases, renewing existing ones and even
                // for rebinding.
                rsp = processRequest(query);
                break;

            case DHCPRELEASE:
                processRelease(query);
                break;

            case DHCPDECLINE:
                processDecline(query);
                break;

            case DHCPINFORM:
                processInform(query);
                break;

            default:
                // Only action is to output a message if debug is enabled,
                // and that is covered by the debug statement before the
                // "switch" statement.
                ;
312
            }
313
314
315
316
317
318
319
320
321
322
323
324
325
326
        } catch (const isc::Exception& e) {

            // Catch-all exception (at least for ones based on the isc
            // Exception class, which covers more or less all that
            // are explicitly raised in the BIND 10 code).  Just log
            // the problem and ignore the packet. (The problem is logged
            // as a debug message because debug is disabled by default -
            // it prevents a DDOS attack based on the sending of problem
            // packets.)
            if (dhcp4_logger.isDebugEnabled(DBG_DHCP4_BASIC)) {
                std::string source = "unknown";
                HWAddrPtr hwptr = query->getHWAddr();
                if (hwptr) {
                    source = hwptr->toText();
327
                }
328
329
330
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC,
                          DHCP4_PACKET_PROCESS_FAIL)
                    .arg(source).arg(e.what());
331
            }
332
        }
333

334
335
336
        if (!rsp) {
            continue;
        }
337

Tomek Mrugalski's avatar
Tomek Mrugalski committed
338
339
340
341
342
        // Let's do class specific processing. This is done before
        // pkt4_send.
        //
        /// @todo: decide whether we want to add a new hook point for
        /// doing class specific processing.
343
344
345
346
347
348
349
        if (!classSpecificProcessing(query, rsp)) {
            /// @todo add more verbosity here
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CLASS_PROCESSING_FAILED);

            continue;
        }

350
351
        // Specifies if server should do the packing
        bool skip_pack = false;
352

Tomek Mrugalski's avatar
Tomek Mrugalski committed
353
        // Execute all callouts registered for pkt4_send
354
355
        if (HooksManager::calloutsPresent(hook_index_pkt4_send_)) {
            CalloutHandlePtr callout_handle = getCalloutHandle(query);
356

357
358
            // Delete all previous arguments
            callout_handle->deleteAllArguments();
359

360
361
            // Clear skip flag if it was set in previous callouts
            callout_handle->setSkip(false);
362

363
364
            // Set our response
            callout_handle->setArgument("response4", rsp);
365

366
367
368
            // Call all installed callouts
            HooksManager::callCallouts(hook_index_pkt4_send_,
                                       *callout_handle);
369

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
            // Callouts decided to skip the next processing step. The next
            // processing step would to send the packet, so skip at this
            // stage means "drop response".
            if (callout_handle->getSkip()) {
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS, DHCP4_HOOK_PACKET_SEND_SKIP);
                skip_pack = true;
            }
        }

        if (!skip_pack) {
            try {
                rsp->pack();
            } catch (const std::exception& e) {
                LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
                    .arg(e.what());
            }
        }

        try {
            // Now all fields and options are constructed into output wire buffer.
            // Option objects modification does not make sense anymore. Hooks
            // can only manipulate wire buffer at this stage.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
392
            // Let's execute all callouts registered for buffer4_send
393
            if (HooksManager::calloutsPresent(Hooks.hook_index_buffer4_send_)) {
394
                CalloutHandlePtr callout_handle = getCalloutHandle(query);
395

396
397
                // Delete previously set arguments
                callout_handle->deleteAllArguments();
398

399
400
                // Pass incoming packet as argument
                callout_handle->setArgument("response4", rsp);
401

402
                // Call callouts
Tomek Mrugalski's avatar
Tomek Mrugalski committed
403
404
                HooksManager::callCallouts(Hooks.hook_index_buffer4_send_,
                                           *callout_handle);
405

406
407
408
409
                // Callouts decided to skip the next processing step. The next
                // processing step would to parse the packet, so skip at this
                // stage means drop.
                if (callout_handle->getSkip()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
410
411
                    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_HOOKS,
                              DHCP4_HOOK_BUFFER_SEND_SKIP);
412
                    continue;
413
                }
414
415

                callout_handle->getArgument("response4", rsp);
416
            }
417
418
419
420
421
422
423
424
425

            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
                      DHCP4_RESPONSE_DATA)
                .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());

            sendPacket(rsp);
        } catch (const std::exception& e) {
            LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
                .arg(e.what());
426
        }
427
428
429
430
431
    }

    return (true);
}

432
string
433
Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    if (!srvid) {
        isc_throw(BadValue, "NULL pointer passed to srvidToString()");
    }
    boost::shared_ptr<Option4AddrLst> generated =
        boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
    if (!srvid) {
        isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
    }

    Option4AddrLst::AddressContainer addrs = generated->getAddresses();
    if (addrs.size() != 1) {
        isc_throw(BadValue, "Malformed option passed to srvidToString(). "
                  << "Expected to contain a single IPv4 address.");
    }

    return (addrs[0].toText());
450
451
}

452
isc::dhcp_ddns::D2Dhcid
453
454
455
456
457
458
459
460
461
Dhcpv4Srv::computeDhcid(const Lease4Ptr& lease) {
    if (!lease) {
        isc_throw(DhcidComputeError, "a pointer to the lease must be not"
                  " NULL to compute DHCID");

    } else if (lease->hostname_.empty()) {
        isc_throw(DhcidComputeError, "unable to compute the DHCID for the"
                  " lease which has empty hostname set");

462
463
    }

464
465
466
467
468
469
470
471
472
    // In order to compute DHCID the client's hostname must be encoded in
    // canonical wire format. It is unlikely that the FQDN is malformed
    // because it is validated by the classes which encapsulate options
    // carrying client FQDN. However, if the client name was carried in the
    // Hostname option it is more likely as it carries the hostname as a
    // regular string.
    std::vector<uint8_t> fqdn_wire;
    try {
        OptionDataTypeUtil::writeFqdn(lease->hostname_, fqdn_wire, true);
473

474
475
476
    } catch (const Exception& ex) {
        isc_throw(DhcidComputeError, "unable to compute DHCID because the"
                  " hostname: " << lease->hostname_ << " is invalid");
477
478

    }
479
480
481

    // Prefer client id to HW address to compute DHCID. If Client Id is
    // NULL, use HW address.
482
    try {
483
484
485
486
487
488
489
        if (lease->client_id_) {
            return (D2Dhcid(lease->client_id_->getClientId(), fqdn_wire));

        } else {
            HWAddrPtr hwaddr(new HWAddr(lease->hwaddr_, HTYPE_ETHER));
            return (D2Dhcid(hwaddr, fqdn_wire));
        }
490
    } catch (const Exception& ex) {
491
492
        isc_throw(DhcidComputeError, "unable to compute DHCID: "
                  << ex.what());
493

494
    }
495
496
497

}

498
499
void
Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
500
501
502
    answer->setIface(question->getIface());
    answer->setIndex(question->getIndex());
    answer->setCiaddr(question->getCiaddr());
503

504
    answer->setSiaddr(IOAddress("0.0.0.0")); // explicitly set this to 0
505
    answer->setHops(question->getHops());
506
507

    // copy MAC address
508
    answer->setHWAddr(question->getHWAddr());
509
510

    // relay address
511
512
    answer->setGiaddr(question->getGiaddr());

513
    // Let's copy client-id to response. See RFC6842.
514
515
    // It is possible to disable RFC6842 to keep backward compatibility
    bool echo = CfgMgr::instance().echoClientId();
516
    OptionPtr client_id = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
517
    if (client_id && echo) {
518
519
        answer->addOption(client_id);
    }
520
521

    // If src/dest HW addresses are used by the packet filtering class
522
523
524
525
526
527
    // we need to copy them as well. There is a need to check that the
    // address being set is not-NULL because an attempt to set the NULL
    // HW would result in exception. If these values are not set, the
    // the default HW addresses (zeroed) should be generated by the
    // packet filtering class when creating Ethernet header for
    // outgoing packet.
528
529
    HWAddrPtr src_hw_addr = question->getLocalHWAddr();
    if (src_hw_addr) {
530
        answer->setLocalHWAddr(src_hw_addr);
531
    }
532
    HWAddrPtr dst_hw_addr = question->getRemoteHWAddr();
533
    if (dst_hw_addr) {
534
        answer->setRemoteHWAddr(dst_hw_addr);
535
    }
536
537
538
539
540
541

    // If this packet is relayed, we want to copy Relay Agent Info option
    OptionPtr rai = question->getOption(DHO_DHCP_AGENT_OPTIONS);
    if (rai) {
        answer->addOption(rai);
    }
542
543
}

544
545
void
Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
546
    OptionPtr opt;
547
548

    // add Message Type Option (type 53)
549
    msg->setType(msg_type);
550
551
552
553

    // more options will be added here later
}

554
555
556
557
558
void
Dhcpv4Srv::appendServerID(const Pkt4Ptr& response) {
    // The source address for the outbound message should have been set already.
    // This is the address that to the best of the server's knowledge will be
    // available from the client.
559
560
    /// @todo: perhaps we should consider some more sophisticated server id
    /// generation, but for the current use cases, it should be ok.
561
562
563
564
565
    response->addOption(OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
                                                     response->getLocalAddr()))
                        );
}

566
567
void
Dhcpv4Srv::appendRequestedOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
568
569
570
571
572
573
574
575
576
577
578

    // Get the subnet relevant for the client. We will need it
    // to get the options associated with it.
    Subnet4Ptr subnet = selectSubnet(question);
    // If we can't find the subnet for the client there is no way
    // to get the options to be sent to a client. We don't log an
    // error because it will be logged by the assignLease method
    // anyway.
    if (!subnet) {
        return;
    }
579

580
581
582
583
584
585
586
587
588
    // try to get the 'Parameter Request List' option which holds the
    // codes of requested options.
    OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
        OptionUint8Array>(question->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
    // If there is no PRL option in the message from the client then
    // there is nothing to do.
    if (!option_prl) {
        return;
    }
589

590
591
592
593
594
595
    // Get the codes of requested options.
    const std::vector<uint8_t>& requested_opts = option_prl->getValues();
    // For each requested option code get the instance of the option
    // to be returned to the client.
    for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
         opt != requested_opts.end(); ++opt) {
596
597
598
599
600
601
        if (!msg->getOption(*opt)) {
            Subnet::OptionDescriptor desc =
                subnet->getOptionDescriptor("dhcp4", *opt);
            if (desc.option && !msg->getOption(*opt)) {
                msg->addOption(desc.option);
            }
602
603
        }
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
604
}
605

606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
void
Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer) {
    // Get the configured subnet suitable for the incoming packet.
    Subnet4Ptr subnet = selectSubnet(question);
    // Leave if there is no subnet matching the incoming packet.
    // There is no need to log the error message here because
    // it will be logged in the assignLease() when it fails to
    // pick the suitable subnet. We don't want to duplicate
    // error messages in such case.
    if (!subnet) {
        return;
    }

    // Try to get the vendor option
    boost::shared_ptr<OptionVendor> vendor_req =
        boost::dynamic_pointer_cast<OptionVendor>(question->getOption(DHO_VIVSO_SUBOPTIONS));
    if (!vendor_req) {
        return;
    }

    uint32_t vendor_id = vendor_req->getVendorId();

    // Let's try to get ORO within that vendor-option
629
630
    /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other
    /// vendors may have different policies.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
631
632
    OptionUint8ArrayPtr oro =
        boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
633
634
635
636
637
638
639
640
641
642

    // Option ORO not found. Don't do anything then.
    if (!oro) {
        return;
    }

    boost::shared_ptr<OptionVendor> vendor_rsp(new OptionVendor(Option::V4, vendor_id));

    // Get the list of options that client requested.
    bool added = false;
643
    const std::vector<uint8_t>& requested_opts = oro->getValues();
644

Tomek Mrugalski's avatar
Tomek Mrugalski committed
645
    for (std::vector<uint8_t>::const_iterator code = requested_opts.begin();
646
         code != requested_opts.end(); ++code) {
647
648
649
650
651
652
653
        if  (!vendor_rsp->getOption(*code)) {
            Subnet::OptionDescriptor desc = subnet->getVendorOptionDescriptor(vendor_id,
                                                                              *code);
            if (desc.option) {
                vendor_rsp->addOption(desc.option);
                added = true;
            }
654
655
        }

656
657
        if (added) {
            answer->addOption(vendor_rsp);
658
659
        }
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
660
}
661

662

663
664
665
666
667
668
669
670
void
Dhcpv4Srv::appendBasicOptions(const Pkt4Ptr& question, Pkt4Ptr& msg) {
    // Identify options that we always want to send to the
    // client (if they are configured).
    static const uint16_t required_options[] = {
        DHO_ROUTERS,
        DHO_DOMAIN_NAME_SERVERS,
        DHO_DOMAIN_NAME };
671

672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
    static size_t required_options_size =
        sizeof(required_options) / sizeof(required_options[0]);

    // Get the subnet.
    Subnet4Ptr subnet = selectSubnet(question);
    if (!subnet) {
        return;
    }

    // Try to find all 'required' options in the outgoing
    // message. Those that are not present will be added.
    for (int i = 0; i < required_options_size; ++i) {
        OptionPtr opt = msg->getOption(required_options[i]);
        if (!opt) {
            // Check whether option has been configured.
            Subnet::OptionDescriptor desc =
                subnet->getOptionDescriptor("dhcp4", required_options[i]);
            if (desc.option) {
                msg->addOption(desc.option);
            }
        }
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
694
}
695

696
697
698
699
700
void
Dhcpv4Srv::processClientName(const Pkt4Ptr& query, Pkt4Ptr& answer) {
    // It is possible that client has sent both Client FQDN and Hostname
    // option. In such case, server should prefer Client FQDN option and
    // ignore the Hostname option.
701
702
703
704
705
706
707
    try {
        Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>
            (query->getOption(DHO_FQDN));
        if (fqdn) {
            processClientFqdnOption(fqdn, answer);

        } else {
708
            OptionStringPtr hostname = boost::dynamic_pointer_cast<OptionString>
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
                (query->getOption(DHO_HOST_NAME));
            if (hostname) {
                processHostnameOption(hostname, answer);
            }
        }
    } catch (const Exception& ex) {
        // In some rare cases it is possible that the client's name processing
        // fails. For example, the Hostname option may be malformed, or there
        // may be an error in the server's logic which would cause multiple
        // attempts to add the same option to the response message. This
        // error message aggregates all these errors so they can be diagnosed
        // from the log. We don't want to throw an exception here because,
        // it will impact the processing of the whole packet. We rather want
        // the processing to continue, even if the client's name is wrong.
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_CLIENT_NAME_PROC_FAIL)
            .arg(ex.what());
725
726
727
728
    }
}

void
729
730
731
732
733
734
Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
                                   Pkt4Ptr& answer) {
    // Create the DHCPv4 Client FQDN Option to be included in the server's
    // response to a client.
    Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));

735
736
737
738
    // Set the server S, N, and O flags based on client's flags and
    // current configuration.
    D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
    d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp);
739

740
741
742
    // Carry over the client's E flag.
    fqdn_resp->setFlag(Option4ClientFqdn::FLAG_E,
                       fqdn->getFlag(Option4ClientFqdn::FLAG_E));
743
744


745
746
747
    // Adjust the domain name based on domain name value and type sent by the
    // client and current configuration.
    d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp);
748
749
750
751
752
753
754
755
756
757
758
759
760

    // Add FQDN option to the response message. Note that, there may be some
    // cases when server may choose not to include the FQDN option in a
    // response to a client. In such cases, the FQDN should be removed from the
    // outgoing message. In theory we could cease to include the FQDN option
    // in this function until it is confirmed that it should be included.
    // However, we include it here for simplicity. Functions used to acquire
    // lease for a client will scan the response message for FQDN and if it
    // is found they will take necessary actions to store the FQDN information
    // in the lease database as well as to generate NameChangeRequests to DNS.
    // If we don't store the option in the reponse message, we will have to
    // propagate it in the different way to the functions which acquire the
    // lease. This would require modifications to the API of this class.
761
    answer->addOption(fqdn_resp);
762
763
764
}

void
765
Dhcpv4Srv::processHostnameOption(const OptionStringPtr& opt_hostname,
766
                                 Pkt4Ptr& answer) {
767
768
769
    // Fetch D2 configuration.
    D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();

770
    // Do nothing if the DNS updates are disabled.
771
    if (!d2_mgr.ddnsEnabled()) {
772
773
774
        return;
    }

775
    std::string hostname = isc::util::str::trim(opt_hostname->getValue());
776
    unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
777
    // The hostname option sent by the client should be at least 1 octet long.
778
779
780
    // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
    /// @todo It would be more liberal to accept this and let it fall into
    /// the case  of replace or less than two below.
781
782
783
784
    if (label_count == 0) {
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME);
        return;
    }
785
786
787
788
    // Copy construct the hostname provided by the client. It is entirely
    // possible that we will use the hostname option provided by the client
    // to perform the DNS update and we will send the same option to him to
    // indicate that we accepted this hostname.
789
    OptionStringPtr opt_hostname_resp(new OptionString(*opt_hostname));
790
791
792
793
794
795
796
797

    // The hostname option may be unqualified or fully qualified. The lab_count
    // holds the number of labels for the name. The number of 1 means that
    // there is only root label "." (even for unqualified names, as the
    // getLabelCount function treats each name as a fully qualified one).
    // By checking the number of labels present in the hostname we may infer
    // whether client has sent the fully qualified or unqualified hostname.

798
799
800
801
802
803
804
805
    /// @todo We may want to reconsider whether it is appropriate for the
    /// client to send a root domain name as a Hostname. There are
    /// also extensions to the auto generation of the client's name,
    /// e.g. conversion to the puny code which may be considered at some point.
    /// For now, we just remain liberal and expect that the DNS will handle
    /// conversion if needed and possible.
    if ((d2_mgr.getD2ClientConfig()->getReplaceClientName()) ||
        (label_count < 2)) {
806
807
808
        // Set to root domain to signal later on that we should replace it.
        // DHO_HOST_NAME is a string option which cannot be empty.
        opt_hostname_resp->setValue(".");
809
    } else if (label_count == 2) {
810
811
812
        // If there are two labels, it means that the client has specified
        // the unqualified name. We have to concatenate the unqalified name
        // with the domain name.
813
        opt_hostname_resp->setValue(d2_mgr.qualifyName(hostname));
814
815
816
    }

    answer->addOption(opt_hostname_resp);
817
818
}

819
820
void
Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
821
                                    const Lease4Ptr& old_lease) {
822
823
824
825
826
    if (!lease) {
        isc_throw(isc::Unexpected,
                  "NULL lease specified when creating NameChangeRequest");
    }

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
    // If old lease is not NULL, it is an indication that the lease has
    // just been renewed. In such case we may need to generate the
    // additional NameChangeRequest to remove an existing entry before
    // we create a NameChangeRequest to add the entry for an updated lease.
    // We may also decide not to generate any requests at all. This is when
    // we discover that nothing has changed in the client's FQDN data.
    if (old_lease) {
        if (!lease->matches(*old_lease)) {
            isc_throw(isc::Unexpected,
                      "there is no match between the current instance of the"
                      " lease: " << lease->toText() << ", and the previous"
                      " instance: " << lease->toText());
        } else {
            // There will be a NameChangeRequest generated to remove existing
            // DNS entries if the following conditions are met:
            // - The hostname is set for the existing lease, we can't generate
            //   removal request for non-existent hostname.
            // - A server has performed reverse, forward or both updates.
            // - FQDN data between the new and old lease do not match.
846
            if (!lease->hasIdenticalFqdn(*old_lease)) {
847
848
                queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
                                       old_lease);
849
850

            // If FQDN data from both leases match, there is no need to update.
851
            } else if (lease->hasIdenticalFqdn(*old_lease)) {
852
                return;
853

854
855
856
857
858
            }

        }
    }

859
860
861
862
    // We may need to generate the NameChangeRequest for the new lease. It
    // will be generated only if hostname is set and if forward or reverse
    // update has been requested.
    queueNameChangeRequest(isc::dhcp_ddns::CHG_ADD, lease);
863
864
}

865
866
void
Dhcpv4Srv::
867
868
869
870
871
872
873
874
queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
                       const Lease4Ptr& lease) {
    // The hostname must not be empty, and at least one type of update
    // should be requested.
    if (!lease || lease->hostname_.empty() ||
        (!lease->fqdn_rev_ && !lease->fqdn_fwd_)) {
        return;
    }
875
876

    // Create the DHCID for the NameChangeRequest.
877
878
879
880
881
882
883
884
885
    D2Dhcid dhcid;
    try {
        dhcid  = computeDhcid(lease);
    } catch (const DhcidComputeError& ex) {
        LOG_ERROR(dhcp4_logger, DHCP4_DHCID_COMPUTE_ERROR)
            .arg(lease->toText())
            .arg(ex.what());
        return;
    }
886

887
    // Create NameChangeRequest
888
889
890
891
892
893
894
895
896
    NameChangeRequestPtr ncr(new NameChangeRequest(chg_type, lease->fqdn_fwd_,
                                                   lease->fqdn_rev_,
                                                   lease->hostname_,
                                                   lease->addr_.toText(),
                                                   dhcid,
                                                   (lease->cltt_ +
                                                    lease->valid_lft_),
                                                   lease->valid_lft_));

897
898
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUEUE_NCR)
        .arg(chg_type == CHG_ADD ? "add" : "remove")
899
        .arg(ncr->toText());
900

901
902
    // And pass it to the the manager.
    CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
903
}
904

905
906
void
Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
907
908
909
910
911
912
913
914
915
916
917
918

    // We need to select a subnet the client is connected in.
    Subnet4Ptr subnet = selectSubnet(question);
    if (!subnet) {
        // This particular client is out of luck today. We do not have
        // information about the subnet he is connected to. This likely means
        // misconfiguration of the server (or some relays). We will continue to
        // process this message, but our response will be almost useless: no
        // addresses or prefixes, no subnet specific configuration etc. The only
        // thing this client can get is some global information (like DNS
        // servers).

919
920
        // perhaps this should be logged on some higher level? This is most
        // likely configuration bug.
921
922
923
        LOG_ERROR(dhcp4_logger, DHCP4_SUBNET_SELECTION_FAILED)
            .arg(question->getRemoteAddr().toText())
            .arg(serverReceivedPacketName(question->getType()));
924
        answer->setType(DHCPNAK);
925
926
927
        answer->setYiaddr(IOAddress("0.0.0.0"));
        return;
    }
928

929
930
931
932
    // Set up siaddr. Perhaps assignLease is not the best place to call this
    // as siaddr has nothing to do with a lease, but otherwise we would have
    // to select subnet twice (performance hit) or update too many functions
    // at once.
933
    /// @todo: move subnet selection to a common code
934
935
    answer->setSiaddr(subnet->getSiaddr());

936
937
938
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
        .arg(subnet->toText());

939
940
941
942
943
944
945
    // Get client-id option
    ClientIdPtr client_id;
    OptionPtr opt = question->getOption(DHO_DHCP_CLIENT_IDENTIFIER);
    if (opt) {
        client_id = ClientIdPtr(new ClientId(opt->getData()));
    }
    // client-id is not mandatory in DHCPv4
946

947
    IOAddress hint = question->getYiaddr();
948

949
    HWAddrPtr hwaddr = question->getHWAddr();
950

951
952
953
954
955
956
    // "Fake" allocation is processing of DISCOVER message. We pretend to do an
    // allocation, but we do not put the lease in the database. That is ok,
    // because we do not guarantee that the user will get that exact lease. If
    // the user selects this server to do actual allocation (i.e. sends REQUEST)
    // it should include this hint. That will help us during the actual lease
    // allocation.
957
    bool fake_allocation = (question->getType() == DHCPDISCOVER);
958

959
960
    CalloutHandlePtr callout_handle = getCalloutHandle(question);

961
962
963
    std::string hostname;
    bool fqdn_fwd = false;
    bool fqdn_rev = false;
964
    OptionStringPtr opt_hostname;
965
966
967
968
    Option4ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
        Option4ClientFqdn>(answer->getOption(DHO_FQDN));
    if (fqdn) {
        hostname = fqdn->getDomainName();
969
970
971
        CfgMgr::instance().getD2ClientMgr().getUpdateDirections(*fqdn,
                                                                fqdn_fwd,
                                                                fqdn_rev);
972
    } else {
973
        opt_hostname = boost::dynamic_pointer_cast<OptionString>
974
            (answer->getOption(DHO_HOST_NAME));
975
        if (opt_hostname) {
976
977
978
979
980
981
982
983
            hostname = opt_hostname->getValue();
            // DHO_HOST_NAME is string option which cannot be blank,
            // we use "." to know we should replace it with a fully
            // generated name. The local string variable needs to be
            // blank in logic below.
            if (hostname == ".") {
                hostname = "";
            }
984
985
            /// @todo It could be configurable what sort of updates the
            /// server is doing when Hostname option was sent.
986
987
988
            fqdn_fwd = true;
            fqdn_rev = true;
        }
989
990
    }

991
992
993
994
    // Use allocation engine to pick a lease for this client. Allocation engine
    // will try to honour the hint, but it is just a hint - some other address
    // may be used instead. If fake_allocation is set to false, the lease will
    // be inserted into the LeaseMgr as well.
995
    /// @todo pass the actual FQDN data.
996
    Lease4Ptr old_lease;
997
    Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
998
999
                                                      hint, fqdn_fwd, fqdn_rev,
                                                      hostname,
1000
1001
1002
                                                    fake_allocation,
                                                    callout_handle,
                                                    old_lease);
1003
1004

    if (lease) {
1005
1006
        // We have a lease! Let's set it in the packet and send it back to
        // the client.
1007
1008
        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
                  DHCP4_LEASE_ADVERT:DHCP4_LEASE_ALLOC)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1009
            .arg(lease->addr_.toText())
1010
            .arg(client_id?client_id->toText():"(no client-id)")
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1011
            .arg(hwaddr?hwaddr->toText():"(no hwaddr info)");
1012
1013
1014

        answer->setYiaddr(lease->addr_);

1015
1016
1017
1018
1019
        // If there has been Client FQDN or Hostname option sent, but the
        // hostname is empty, it means that server is responsible for
        // generating the entire hostname for the client. The example of the
        // client's name, generated from the IP address is: host-192-0-2-3.
        if ((fqdn || opt_hostname) && lease->hostname_.empty()) {
1020
1021
1022
            lease->hostname_ = CfgMgr::instance()
                               .getD2ClientMgr().generateFqdn(lease->addr_);

1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
            // The operations below are rather safe, but we want to catch
            // any potential exceptions (e.g. invalid lease database backend
            // implementation) and log an error.
            try {
                // The lease update should be safe, because the lease should
                // be already in the database. In most cases the exception
                // would be thrown if the lease was missing.
                LeaseMgrFactory::instance().updateLease4(lease);
                // The name update in the option should be also safe,
                // because the generated name is well formed.
                if (fqdn) {
                    fqdn->setDomainName(lease->hostname_,
                                        Option4ClientFqdn::FULL);
                } else if (opt_hostname) {
1037
                    opt_hostname->setValue(lease->hostname_);
1038
1039
1040
1041
1042
1043
1044
1045
                }

            } catch (const Exception& ex) {
                LOG_ERROR(dhcp4_logger, DHCP4_NAME_GEN_UPDATE_FAIL)
                    .arg(ex.what());
            }
        }

1046
1047
1048
1049
1050
1051
1052
1053
        // IP Address Lease time (type 51)
        opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
        opt->setUint32(lease->valid_lft_);
        answer->addOption(opt);

        // Subnet mask (type 1)
        answer->addOption(getNetmaskOption(subnet));

1054
1055
        /// @todo: send renew timer option (T1, option 58)
        /// @todo: send rebind timer option (T2, option 59)
1056

1057
1058
1059
        // Create NameChangeRequests if DDNS is enabled and this is a
        // real allocation.
        if (!fake_allocation && CfgMgr::instance().ddnsEnabled()) {
1060
1061
1062
1063
1064
1065
1066
1067
            try {
                createNameChangeRequests(lease, old_lease);
            } catch (const Exception& ex) {
                LOG_ERROR(dhcp4_logger, DHCP4_NCR_CREATION_FAILED)
                    .arg(ex.what());
            }
        }

1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
    } else {
        // Allocation engine did not allocate a lease. The engine logged
        // cause of that failure. The only thing left is to insert
        // status code to pass the sad news to the client.

        LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, fake_allocation?
                  DHCP4_LEASE_ADVERT_FAIL:DHCP4_LEASE_ALLOC_FAIL)
            .arg(client_id?client_id->toText():"(no client-id)")
            .arg(hwaddr?hwaddr->toText():"(no hwaddr info)")
            .arg(hint.toText());

1079
        answer->setType(DHCPNAK);
1080
        answer->setYiaddr(IOAddress("0.0.0.0"));
1081
1082
1083

        answer->delOption(DHO_FQDN);
        answer->delOption(DHO_HOST_NAME);
1084
1085
1086
    }
}

1087
void
1088
1089
1090
1091
1092
Dhcpv4Srv::adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response) {
    adjustRemoteAddr(query, response);

    // For the non-relayed message, the destination port is the client's port.
    // For the relayed message, the server/relay port is a destination.
1093
1094
1095
1096
1097
1098
1099
    // Note that the call to this function may throw if invalid combination
    // of hops and giaddr is found (hops = 0 if giaddr = 0 and hops != 0 if
    // giaddr != 0). The exception will propagate down and eventually cause the
    // packet to be discarded.
    response->setRemotePort(query->isRelayed() ? DHCP4_SERVER_PORT :
                            DHCP4_CLIENT_PORT);

1100
1101
1102
1103
1104
1105
    // In many cases the query is sent to a broadcast address. This address
    // appears as a local address in the query message. Therefore we can't
    // simply copy local address from the query and use it as a source
    // address for the response. Instead, we have to check what address our
    // socket is bound to and use it as a source address. This operation
    // may throw if for some reason the socket is closed.
1106
1107
    /// @todo Consider an optimization that we use local address from
    /// the query if this address is not broadcast.
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
    SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
    // Set local adddress, port and interface.
    response->setLocalAddr(sock_info.addr_);
    response->setLocalPort(DHCP4_SERVER_PORT);
    response->setIface(query->getIface());
    response->setIndex(query->getIndex());
}

void
Dhcpv4Srv::adjustRemoteAddr(const Pkt4Ptr& question, const Pkt4Ptr& response) {
1118
1119
1120
1121
1122
1123
1124
1125
    // Let's create static objects representing zeroed and broadcast
    // addresses. We will use them further in this function to test
    // other addresses against them. Since they are static, they will
    // be created only once.
    static const IOAddress zero_addr("0.0.0.0");
    static const IOAddress bcast_addr("255.255.255.255");

    // If received relayed message, server responds to the relay address.
1126
    if (question->isRelayed()) {
1127
        response->setRemoteAddr(question->getGiaddr());
1128
1129
1130
1131

    // If giaddr is 0 but client set ciaddr, server should unicast the
    // response to ciaddr.
    } else if (question->getCiaddr() != zero_addr) {
1132
        response->setRemoteAddr(question->getCiaddr());
1133
1134
1135
1136

    // We can't unicast the response to the client when sending NAK,
    // because we haven't allocated address for him. Therefore,
    // NAK is broadcast.
1137
1138
    } else if (response->getType() == DHCPNAK) {
        response->setRemoteAddr(bcast_addr);
1139
1140

    // If yiaddr is set it means that we have created a lease for a client.
1141
    } else if (response->getYiaddr() != zero_addr) {
1142
1143
1144
1145
1146
1147
        // If the broadcast bit is set in the flags field, we have to
        // send the response to broadcast address. Client may have requested it
        // because it doesn't support reception of messages on the interface
        // which doesn't have an address assigned. The other case when response
        // must be broadcasted is when our server does not support responding
        // directly to a client without address assigned.
1148
        const bool bcast_flag = ((question->getFlags() & Pkt4::FLAG_BROADCAST_MASK) != 0);
1149
        if (!IfaceMgr::instance().isDirectResponseSupported() || bcast_flag) {
1150
            response->setRemoteAddr(bcast_addr);
1151
1152
1153
1154
1155

        // Client cleared the broadcast bit and we support direct responses
        // so we should unicast the response to a newly allocated address -
        // yiaddr.
        } else {
1156
            response->setRemoteAddr(response ->getYiaddr());
1157
1158
1159
1160
1161
1162
1163

        }

    // In most cases, we should have the remote address found already. If we
    // found ourselves at this point, the rational thing to do is to respond
    // to the address we got the query from.
    } else {
1164
        response->setRemoteAddr(question->getRemoteAddr());
1165
1166
1167
1168
1169

    }
}


1170
1171
OptionPtr
Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
1172
1173
1174
1175
1176
1177
1178
1179
    uint32_t netmask = getNetmask4(subnet->get().second);

    OptionPtr opt(new OptionInt<uint32_t>(Option::V4,
                  DHO_SUBNET_MASK, netmask));

    return (opt);
}

1180
1181
Pkt4Ptr
Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
1182
1183
1184

    sanityCheck(discover, FORBIDDEN);

1185
    Pkt4Ptr offer = Pkt4Ptr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1186
        (new Pkt4(DHCPOFFER, discover->getTransid()));
1187

Tomek Mrugalski's avatar
Tomek Mrugalski committed
1188
1189
1190
    copyDefaultFields(discover, offer);
    appendDefaultOptions(offer, DHCPOFFER);

1191
1192
1193
1194
1195
1196
    // If DISCOVER message contains the FQDN or Hostname option, server
    // may respond to the client with the appropriate FQDN or Hostname
    // option to indicate that whether it will take responsibility for
    // updating DNS when the client sends REQUEST message.
    processClientName(discover, offer);

1197
    assignLease(discover, offer);
1198

1199
1200
1201
    // Adding any other options makes sense only when we got the lease.
    if (offer->getYiaddr() != IOAddress("0.0.0.0")) {
        appendRequestedOptions(discover, offer);
1202
        appendRequestedVendorOptions(discover, offer);
1203
1204
1205
1206
1207
        // There are a few basic options that we always want to
        // include in the response. If client did not request
        // them we append them for him.
        appendBasicOptions(discover, offer);
    }
1208

1209
1210
1211
1212
1213
1214
    // Set the src/dest IP address, port and interface for the outgoing
    // packet.
    adjustIfaceData(discover, offer);

    appendServerID(offer);

1215
    return (offer);
1216
1217
}

1218
1219
Pkt4Ptr
Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
1220

Tomek Mrugalski's avatar
Tomek Mrugalski committed
1221
    /// @todo Uncomment this (see ticket #3116)