dhcp6_srv.cc 41.4 KB
Newer Older
1
// Copyright (C) 2011-2013 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
16
#include <config.h>

17
#include <asiolink/io_address.h>
18
#include <dhcp/dhcp6.h>
19
#include <dhcp/duid.h>
20
#include <dhcp/iface_mgr.h>
21
#include <dhcp/libdhcp++.h>
22
#include <dhcp/option6_addrlst.h>
23
#include <dhcp/option6_ia.h>
24
25
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaaddr.h>
26
#include <dhcp/option_custom.h>
27
#include <dhcp/option_int_array.h>
28
#include <dhcp/pkt6.h>
29
30
31
32
33
34
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet.h>
35
#include <dhcpsrv/utils.h>
36
#include <exceptions/exceptions.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
37
#include <util/io_utilities.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
38
#include <util/range_utilities.h>
39
#include <util/encode/hex.h>
40

41
#include <boost/foreach.hpp>
42
43
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string/erase.hpp>
44

45
46
#include <stdlib.h>
#include <time.h>
47
48
#include <iomanip>
#include <fstream>
49

50
using namespace isc;
51
using namespace isc::asiolink;
52
using namespace isc::dhcp;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
53
using namespace isc::util;
54
using namespace std;
55

56
57
namespace isc {
namespace dhcp {
58

59
60
61
62
63
64
65
66
67
68
/// @brief file name of a server-id file
///
/// Server must store its duid in persistent storage that must not change
/// between restarts. This is name of the file that is created in dataDir
/// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
/// double digit hex values separated by colons format, e.g.
/// 01:ff:02:03:06:80:90:ab:cd:ef. Server will create it during first
/// run and then use it afterwards.
static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";

69
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
70
    : alloc_engine_(), serverid_(), shutdown_(true), port_(port) {
71

72
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
73

74
    // Initialize objects required for DHCP server operation.
75
    try {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
76
77
        // Port 0 is used for testing purposes. It means that the server should
        // not open any sockets at all. Some tests, e.g. configuration parser,
78
79
        // require Dhcpv6Srv object, but they don't really need it to do
        // anything. This speed up and simplifies the tests.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
80
        if (port > 0) {
81
82
83
84
            if (IfaceMgr::instance().countIfaces() == 0) {
                LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
                return;
            }
85
            IfaceMgr::instance().openSockets6(port_);
86
        }
87

88
89
90
        string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
        if (loadServerID(duid_file)) {
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_SERVERID_LOADED)
91
                .arg(duidToString(getServerID()))
92
93
94
95
96
97
98
99
100
101
102
103
104
                .arg(duid_file);
        } else {
            generateServerID();
            LOG_INFO(dhcp6_logger, DHCP6_SERVERID_GENERATED)
                .arg(duidToString(getServerID()))
                .arg(duid_file);

            if (!writeServerID(duid_file)) {
                LOG_WARN(dhcp6_logger, DHCP6_SERVERID_WRITE_FAIL)
                    .arg(duid_file);
            }

        }
105

106
107
108
        // Instantiate allocation engine
        alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));

109
    } catch (const std::exception &e) {
110
        LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
111
112
        return;
    }
113

114
115
    // All done, so can proceed
    shutdown_ = false;
116
117
}

118
Dhcpv6Srv::~Dhcpv6Srv() {
119
    IfaceMgr::instance().closeSockets();
120

121
    LeaseMgrFactory::destroy();
122
123
}

124
void Dhcpv6Srv::shutdown() {
125
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SHUTDOWN_REQUEST);
126
127
128
    shutdown_ = true;
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
129
bool Dhcpv6Srv::run() {
130
    while (!shutdown_) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
131
132
133
134
135
136
        /// @todo: calculate actual timeout to the next event (e.g. lease
        /// expiration) once we have lease database. The idea here is that
        /// it is possible to do everything in a single process/thread.
        /// For now, we are just calling select for 1000 seconds. There
        /// were some issues reported on some systems when calling select()
        /// with too large values. Unfortunately, I don't recall the details.
137
138
        //cppcheck-suppress variableScope This is temporary anyway
        const int timeout = 1000;
139

Tomek Mrugalski's avatar
Tomek Mrugalski committed
140
        // client's message and server's response
141
        Pkt6Ptr query;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
142
        Pkt6Ptr rsp;
143

144
145
        try {
            query = IfaceMgr::instance().receive6(timeout);
Marcin Siodelski's avatar
Marcin Siodelski committed
146
        } catch (const std::exception& e) {
147
148
149
            LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
        }

150
151
        if (query) {
            if (!query->unpack()) {
152
153
                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
                          DHCP6_PACKET_PARSE_FAIL);
154
155
                continue;
            }
156
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
157
                      .arg(query->getName());
158
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
159
                      .arg(static_cast<int>(query->getType()))
160
161
162
                      .arg(query->getBuffer().getLength())
                      .arg(query->toText());

163
164
165
166
167
            try {
                switch (query->getType()) {
                case DHCPV6_SOLICIT:
                    rsp = processSolicit(query);
                    break;
168

169
170
171
                case DHCPV6_REQUEST:
                    rsp = processRequest(query);
                    break;
172

173
174
175
                case DHCPV6_RENEW:
                    rsp = processRenew(query);
                    break;
176

177
178
179
                case DHCPV6_REBIND:
                    rsp = processRebind(query);
                    break;
180

181
182
183
                case DHCPV6_CONFIRM:
                    rsp = processConfirm(query);
                    break;
184

185
                case DHCPV6_RELEASE:
186
187
                    rsp = processRelease(query);
                    break;
188

189
190
191
                case DHCPV6_DECLINE:
                    rsp = processDecline(query);
                    break;
192

193
194
195
                case DHCPV6_INFORMATION_REQUEST:
                    rsp = processInfRequest(query);
                    break;
196

197
198
199
200
201
202
                default:
                    // Only action is to output a message if debug is enabled,
                    // and that will be covered by the debug statement before
                    // the "switch" statement.
                    ;
                }
203

204
205
206
207
208
            } catch (const RFCViolation& e) {
                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_REQUIRED_OPTIONS_CHECK_FAIL)
                    .arg(query->getName())
                    .arg(query->getRemoteAddr())
                    .arg(e.what());
209
210
211
212
213
214
215
216
217
218
219
220
221
222

            } 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.)
                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_PACKET_PROCESS_FAIL)
                    .arg(query->getName())
                    .arg(query->getRemoteAddr())
                    .arg(e.what());
223
224
            }

225
            if (rsp) {
226
227
228
229
230
231
                rsp->setRemoteAddr(query->getRemoteAddr());
                rsp->setLocalAddr(query->getLocalAddr());
                rsp->setRemotePort(DHCP6_CLIENT_PORT);
                rsp->setLocalPort(DHCP6_SERVER_PORT);
                rsp->setIndex(query->getIndex());
                rsp->setIface(query->getIface());
232
233
234

                LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA,
                          DHCP6_RESPONSE_DATA)
235
                    .arg(static_cast<int>(rsp->getType())).arg(rsp->toText());
236
237

                if (rsp->pack()) {
238
239
                    try {
                        IfaceMgr::instance().send(rsp);
Marcin Siodelski's avatar
Marcin Siodelski committed
240
241
                    } catch (const std::exception& e) {
                        LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
242
                    }
243
244
                } else {
                    LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL);
245
                }
246
            }
247
248
249
        }
    }

250
    return (true);
251
}
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
bool Dhcpv6Srv::loadServerID(const std::string& file_name) {

    // load content of the file into a string
    fstream f(file_name.c_str(), ios::in);
    if (!f.is_open()) {
        return (false);
    }

    string hex_string;
    f >> hex_string;
    f.close();

    // remove any spaces
    boost::algorithm::erase_all(hex_string, " ");

    // now remove :
    /// @todo: We should check first if the format is sane.
    /// Otherwise 1:2:3:4 will be converted to 0x12, 0x34
    boost::algorithm::erase_all(hex_string, ":");

    std::vector<uint8_t> bin;

    // Decode the hex string and store it in bin (which happens
    // to be OptionBuffer format)
    isc::util::encode::decodeHex(hex_string, bin);

    // Now create server-id option
    serverid_.reset(new Option(Option::V6, D6O_SERVERID, bin));

    return (true);
}

285
286
std::string
Dhcpv6Srv::duidToString(const OptionPtr& opt) {
287
    stringstream tmp;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
288

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    OptionBuffer data = opt->getData();

    bool colon = false;
    for (OptionBufferConstIter it = data.begin(); it != data.end(); ++it) {
        if (colon) {
            tmp << ":";
        }
        tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(*it);
        if (!colon) {
            colon = true;
        }
    }

    return tmp.str();
}

305
306
bool
Dhcpv6Srv::writeServerID(const std::string& file_name) {
307
308
309
310
311
312
    fstream f(file_name.c_str(), ios::out | ios::trunc);
    if (!f.good()) {
        return (false);
    }
    f << duidToString(getServerID());
    f.close();
313
    return (true);
314
}
Tomek Mrugalski's avatar
Tomek Mrugalski committed
315

316
317
void
Dhcpv6Srv::generateServerID() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
318
319
320
321
322
323
324

    /// @todo: This code implements support for DUID-LLT (the recommended one).
    /// We should eventually add support for other DUID types: DUID-LL, DUID-EN
    /// and DUID-UUID

    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();

Tomek Mrugalski's avatar
Tomek Mrugalski committed
325
    // Let's find suitable interface.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
326
327
    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
         iface != ifaces.end(); ++iface) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
328
        // All the following checks could be merged into one multi-condition
Tomek Mrugalski's avatar
Tomek Mrugalski committed
329
330
331
332
        // statement, but let's keep them separated as perhaps one day
        // we will grow knobs to selectively turn them on or off. Also,
        // this code is used only *once* during first start on a new machine
        // and then server-id is stored. (or at least it will be once
333
        // DUID storage is implemented)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
334
335
336

        // I wish there was a this_is_a_real_physical_interface flag...

Tomek Mrugalski's avatar
Tomek Mrugalski committed
337
338
339
340
341
        // MAC address should be at least 6 bytes. Although there is no such
        // requirement in any RFC, all decent physical interfaces (Ethernet,
        // WiFi, Infiniband, etc.) have 6 bytes long MAC address. We want to
        // base our DUID on real hardware address, rather than virtual
        // interface that pretends that underlying IP address is its MAC.
342
        if (iface->getMacLen() < MIN_MAC_LEN) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
343
344
345
            continue;
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
346
        // Let's don't use loopback.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
347
348
349
350
        if (iface->flag_loopback_) {
            continue;
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
351
        // Let's skip downed interfaces. It is better to use working ones.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
352
353
354
355
        if (!iface->flag_up_) {
            continue;
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
356
        // Some interfaces (like lo on Linux) report 6-bytes long
357
        // MAC address 00:00:00:00:00:00. Let's not use such weird interfaces
Tomek Mrugalski's avatar
Tomek Mrugalski committed
358
        // to generate DUID.
359
        if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
360
361
362
363
364
365
366
367
368
369
370
            continue;
        }

        // Ok, we have useful MAC. Let's generate DUID-LLT based on
        // it. See RFC3315, Section 9.2 for details.

        // DUID uses seconds since midnight of 01-01-2000, time() returns
        // seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
        time_t seconds = time(NULL);
        seconds -= DUID_TIME_EPOCH;

371
        OptionBuffer srvid(8 + iface->getMacLen());
372
        writeUint16(DUID::DUID_LLT, &srvid[0]);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
373
374
        writeUint16(HWTYPE_ETHERNET, &srvid[2]);
        writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
375
        memcpy(&srvid[0] + 8, iface->getMac(), iface->getMacLen());
Tomek Mrugalski's avatar
Tomek Mrugalski committed
376
377
378
379

        serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
                                         srvid.begin(), srvid.end()));
        return;
380
    }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
381

Tomek Mrugalski's avatar
Tomek Mrugalski committed
382
    // If we reached here, there are no suitable interfaces found.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
383
384
385
386
387
    // Either interface detection is not supported on this platform or
    // this is really weird box. Let's use DUID-EN instead.
    // See Section 9.3 of RFC3315 for details.

    OptionBuffer srvid(12);
388
    writeUint16(DUID::DUID_EN, &srvid[0]);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
389
390
    writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
391
392
    // Length of the identifier is company specific. I hereby declare
    // ISC "standard" of 6 bytes long pseudo-random numbers.
393
    srandom(time(NULL));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
394
    fillRandom(&srvid[6], &srvid[12]);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
395

Tomek Mrugalski's avatar
Tomek Mrugalski committed
396
397
    serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
                                     srvid.begin(), srvid.end()));
398
399
}

400
401
void
Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
402
403
    // Add client-id.
    OptionPtr clientid = question->getOption(D6O_CLIENTID);
404
405
406
    if (clientid) {
        answer->addOption(clientid);
    }
407
408
    /// @todo: Should throw if there is no client-id (except anonymous INF-REQUEST)

409
    // If this is a relayed message, we need to copy relay information
410
411
412
    if (!question->relay_info_.empty()) {
        answer->copyRelayInfo(question);
    }
413

414
415
}

416
void
417
Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
418
419
420
421
    // add server-id
    answer->addOption(getServerID());
}

422
423
void
Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
424
425
426
427
428
429
430
    // Get the configured subnet suitable for the incoming packet.
    Subnet6Ptr 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.
431
432
433
    if (!subnet) {
        return;
    }
434

435
436
    // Client requests some options using ORO option. Try to
    // get this option from client's message.
437
438
    boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
439
440
441
442
443
444
445
    // Option ORO not found. Don't do anything then.
    if (!option_oro) {
        return;
    }
    // Get the list of options that client requested.
    const std::vector<uint16_t>& requested_opts = option_oro->getValues();
    BOOST_FOREACH(uint16_t opt, requested_opts) {
446
447
        Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp6", opt);
        if (desc.option) {
448
449
450
            answer->addOption(desc.option);
        }
    }
451
452
}

453
454
OptionPtr
Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
455
456
457
458
459
460
    // @todo This function uses OptionCustom class to manage contents
    // of the data fields. Since this this option is frequently used
    // it may be good to implement dedicated class to avoid performance
    // impact.

    // Get the definition of the option holding status code.
461
462
    OptionDefinitionPtr status_code_def =
        LibDHCP::getOptionDef(Option::V6, D6O_STATUS_CODE);
463
    // This definition is assumed to be initialized in LibDHCP.
464
465
    assert(status_code_def);

466
    // As there is no dedicated class to represent Status Code
467
468
469
    // the OptionCustom class is used here instead.
    OptionCustomPtr option_status =
        OptionCustomPtr(new OptionCustom(*status_code_def, Option::V6));
470
471
    assert(option_status);

472
    // Set status code to 'code' (0 - means data field #0).
473
    option_status->writeInteger(code, 0);
474
    // Set a message (1 - means data field #1).
475
476
    option_status->writeString(text, 1);
    return (option_status);
477
478
}

479
480
481
void
Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
                       RequirementLevel serverid) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
    Option::OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
    switch (clientid) {
    case MANDATORY:
        if (client_ids.size() != 1) {
            isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
                      << pkt->getName() << ", but " << client_ids.size()
                      << " received");
        }
        break;
    case OPTIONAL:
        if (client_ids.size() > 1) {
            isc_throw(RFCViolation, "Too many (" << client_ids.size()
                      << ") client-id options received in " << pkt->getName());
        }
        break;

    case FORBIDDEN:
        // doesn't make sense - client-id is always allowed
        break;
    }

    Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
    switch (serverid) {
    case FORBIDDEN:
506
        if (!server_ids.empty()) {
507
            isc_throw(RFCViolation, "Server-id option was not expected, but "
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
                      << server_ids.size() << " received in " << pkt->getName());
        }
        break;

    case MANDATORY:
        if (server_ids.size() != 1) {
            isc_throw(RFCViolation, "Invalid number of server-id options received ("
                      << server_ids.size() << "), exactly 1 expected in message "
                      << pkt->getName());
        }
        break;

    case OPTIONAL:
        if (server_ids.size() > 1) {
            isc_throw(RFCViolation, "Too many (" << server_ids.size()
523
                      << ") server-id options received in " << pkt->getName());
524
525
526
527
        }
    }
}

528
529
Subnet6Ptr
Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
530

531
    Subnet6Ptr subnet;
532

533
534
535
536
    if (question->relay_info_.empty()) {
        // This is a direct (non-relayed) message

        // Try to find a subnet if received packet from a directly connected client
537
538
539
540
        subnet = CfgMgr::instance().getSubnet6(question->getIface());
        if (!subnet) {
            // If no subnet was found, try to find it based on remote address
            subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
541
542
        }
    } else {
543

544
545
        // This is a relayed message
        OptionPtr interface_id = question->getAnyRelayOption(D6O_INTERFACE_ID,
546
                                                             Pkt6::RELAY_GET_FIRST);
547
548
549
        if (interface_id) {
            subnet = CfgMgr::instance().getSubnet6(interface_id);
        }
550

551
552
553
554
        if (!subnet) {
            // If no interface-id was specified (or not configured on server), let's
            // try address matching
            IOAddress link_addr = question->relay_info_.back().linkaddr_;
555

556
557
558
559
            // if relay filled in link_addr field, then let's use it
            if (link_addr != IOAddress("::")) {
                subnet = CfgMgr::instance().getSubnet6(link_addr);
            }
560
561
        }
    }
562
563

    return (subnet);
564
565
}

566
567
void
Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
568

Tomek Mrugalski's avatar
Tomek Mrugalski committed
569
570
    // We need to allocate addresses for all IA_NA options in the client's
    // question (i.e. SOLICIT or REQUEST) message.
571
572
    // @todo add support for IA_TA
    // @todo add support for IA_PD
Tomek Mrugalski's avatar
Tomek Mrugalski committed
573
574

    // We need to select a subnet the client is connected in.
575
    Subnet6Ptr subnet = selectSubnet(question);
576
    if (!subnet) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
577
578
579
580
581
582
583
        // 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).
584

585
        LOG_WARN(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED)
586
587
588
            .arg(question->getRemoteAddr().toText())
            .arg(question->getName());

589
590
591
    } else {
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
            .arg(subnet->toText());
592
593
594
595
    }

    // @todo: We should implement Option6Duid some day, but we can do without it
    // just fine for now
Tomek Mrugalski's avatar
Tomek Mrugalski committed
596
597
598
599
600

    // Let's find client's DUID. Client is supposed to include its client-id
    // option almost all the time (the only exception is an anonymous inf-request,
    // but that is mostly a theoretical case). Our allocation engine needs DUID
    // and will refuse to allocate anything to anonymous clients.
601
602
603
604
    DuidPtr duid;
    OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
    if (opt_duid) {
        duid = DuidPtr(new DUID(opt_duid->getData()));
605
    } else {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
606
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CLIENTID_MISSING);
607
608
        // Let's drop the message. This client is not sane.
        isc_throw(RFCViolation, "Mandatory client-id is missing in received message");
609
610
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
611
612
613
614
615
616
    // Now that we have all information about the client, let's iterate over all
    // received options and handle IA_NA options one by one and store our
    // responses in answer message (ADVERTISE or REPLY).
    //
    // @todo: expand this to cover IA_PD and IA_TA once we implement support for
    // prefix delegation and temporary addresses.
617
618
    for (Option::OptionCollection::iterator opt = question->options_.begin();
         opt != question->options_.end(); ++opt) {
619
620
        switch (opt->second->getType()) {
        case D6O_IA_NA: {
621
            OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
622
                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
623
624
625
626
627
628
629
            if (answer_opt) {
                answer->addOption(answer_opt);
            }
            break;
        }
        default:
            break;
630
631
        }
    }
632
}
633

634
635
636
OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                       Pkt6Ptr question, boost::shared_ptr<Option6IA> ia) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
637
638
639
640
    // If there is no subnet selected for handling this IA_NA, the only thing to do left is
    // to say that we are sorry, but the user won't get an address. As a convenience, we
    // use a different status text to indicate that (compare to the same status code,
    // but different wording below)
641
    if (!subnet) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
642
        // Create empty IA_NA option with IAID matching the request.
643
644
645
646
        // Note that we don't use OptionDefinition class to create this option.
        // This is because we prefer using a constructor of Option6IA that
        // initializes IAID. Otherwise we would have to use setIAID() after
        // creation of the option which has some performance implications.
647
648
        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

Tomek Mrugalski's avatar
Tomek Mrugalski committed
649
        // Insert status code NoAddrsAvail.
650
651
652
653
        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no subnet available."));
        return (ia_rsp);
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
654
655
656
    // Check if the client sent us a hint in his IA_NA. Clients may send an
    // address in their IA_NA options as a suggestion (e.g. the last address
    // they used before).
657
    boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
658
                                        (ia->getOption(D6O_IAADDR));
659
660
661
662
663
    IOAddress hint("::");
    if (hintOpt) {
        hint = hintOpt->getAddress();
    }

664
665
666
667
    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
        .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
        .arg(hintOpt?hint.toText():"(no hint)");

Tomek Mrugalski's avatar
Tomek Mrugalski committed
668
669
670
671
672
673
    // "Fake" allocation is processing of SOLICIT 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.
674
675
676
677
678
679
    bool fake_allocation = false;
    if (question->getType() == DHCPV6_SOLICIT) {
        /// @todo: Check if we support rapid commit
        fake_allocation = true;
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
680
681
682
683
    // 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.
684
685
686
    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
                                                      hint, fake_allocation);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
687
    // Create IA_NA that we will put in the response.
688
689
    // Do not use OptionDefinition to create option's instance so
    // as we can initialize IAID using a constructor.
690
691
692
    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

    if (lease) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
693
694
695
696
        // We have a lease! Let's wrap its content into IA_NA option
        // with IAADDR suboption.
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
                  DHCP6_LEASE_ADVERT:DHCP6_LEASE_ALLOC)
697
698
699
            .arg(lease->addr_.toText())
            .arg(duid?duid->toText():"(no-duid)")
            .arg(ia->getIAID());
700
701
702
703
704
705
706
707
708
709

        ia_rsp->setT1(subnet->getT1());
        ia_rsp->setT2(subnet->getT2());

        boost::shared_ptr<Option6IAAddr>
            addr(new Option6IAAddr(D6O_IAADDR,
                                   lease->addr_,
                                   lease->preferred_lft_,
                                   lease->valid_lft_));
        ia_rsp->addOption(addr);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
710
711
712
713

        // It would be possible to insert status code=0(success) as well,
        // but this is considered waste of bandwidth as absence of status
        // code is considered a success.
714
    } else {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
715
716
717
718
        // 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.

719
720
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
                  DHCP6_LEASE_ADVERT_FAIL : DHCP6_LEASE_ALLOC_FAIL)
721
            .arg(duid?duid->toText():"(no-duid)")
722
            .arg(ia->getIAID());
723

Tomek Mrugalski's avatar
Tomek Mrugalski committed
724
725
        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
                          "Sorry, no address could be allocated."));
726
727
728
729
    }
    return (ia_rsp);
}

730
731
OptionPtr
Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
732
                      Pkt6Ptr /* question */, boost::shared_ptr<Option6IA> ia) {
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
    if (!subnet) {
        // There's no subnet select for this client. There's nothing to renew.
        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

        // Insert status code NoAddrsAvail.
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                          "Sorry, no known leases for this duid/iaid."));

        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RENEW_UNKNOWN_SUBNET)
            .arg(duid->toText())
            .arg(ia->getIAID());

        return (ia_rsp);
    }

748
749
    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(*duid, ia->getIAID(),
                                                            subnet->getID());
750

751
752
753
754
755
756
757
    if (!lease) {
        // client renewing a lease that we don't know about.

        // Create empty IA_NA option with IAID matching the request.
        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

        // Insert status code NoAddrsAvail.
758
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
759
                          "Sorry, no known leases for this duid/iaid."));
760
761
762
763
764
765

        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
            .arg(duid->toText())
            .arg(ia->getIAID())
            .arg(subnet->toText());

766
        return (ia_rsp);
767
768
    }

769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
    lease->preferred_lft_ = subnet->getPreferred();
    lease->valid_lft_ = subnet->getValid();
    lease->t1_ = subnet->getT1();
    lease->t2_ = subnet->getT2();
    lease->cltt_ = time(NULL);

    LeaseMgrFactory::instance().updateLease6(lease);

    // Create empty IA_NA option with IAID matching the request.
    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

    ia_rsp->setT1(subnet->getT1());
    ia_rsp->setT2(subnet->getT2());

    boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
                                          lease->addr_, lease->preferred_lft_,
                                          lease->valid_lft_));
    ia_rsp->addOption(addr);
    return (ia_rsp);
}

790
791
void
Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
792
793
794

    // We need to renew addresses for all IA_NA options in the client's
    // RENEW message.
795
796
    // @todo add support for IA_TA
    // @todo add support for IA_PD
797
798
799
800
801
802
803
804
805
806
807
808

    // We need to select a subnet the client is connected in.
    Subnet6Ptr subnet = selectSubnet(renew);
    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).

809
        LOG_WARN(dhcp6_logger, DHCP6_SUBNET_SELECTION_FAILED)
810
811
            .arg(renew->getRemoteAddr().toText())
            .arg(renew->getName());
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
    } else {
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
            .arg(subnet->toText());
    }

    // Let's find client's DUID. Client is supposed to include its client-id
    // option almost all the time (the only exception is an anonymous inf-request,
    // but that is mostly a theoretical case). Our allocation engine needs DUID
    // and will refuse to allocate anything to anonymous clients.
    OptionPtr opt_duid = renew->getOption(D6O_CLIENTID);
    if (!opt_duid) {
        // This should not happen. We have checked this before.
        reply->addOption(createStatusCode(STATUS_UnspecFail,
                         "You did not include mandatory client-id"));
        return;
    }
    DuidPtr duid(new DUID(opt_duid->getData()));

    for (Option::OptionCollection::iterator opt = renew->options_.begin();
         opt != renew->options_.end(); ++opt) {
        switch (opt->second->getType()) {
        case D6O_IA_NA: {
            OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
            if (answer_opt) {
                reply->addOption(answer_opt);
            }
            break;
        }
        default:
            break;
        }
    }
845
846
}

847
848
void
Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
849

850
851
852
853
854
855
856
857
858
    // We need to release addresses for all IA_NA options in the client's
    // RELEASE message.
    // @todo Add support for IA_TA
    // @todo Add support for IA_PD
    // @todo Consider supporting more than one address in a single IA_NA.
    // That was envisaged by RFC3315, but it never happened. The only
    // software that supports that is Dibbler, but its author seriously doubts
    // if anyone is really using it. Clients that want more than one address
    // just include more instances of IA_NA options.
859

860
861
862
863
864
865
866
    // Let's find client's DUID. Client is supposed to include its client-id
    // option almost all the time (the only exception is an anonymous inf-request,
    // but that is mostly a theoretical case). Our allocation engine needs DUID
    // and will refuse to allocate anything to anonymous clients.
    OptionPtr opt_duid = release->getOption(D6O_CLIENTID);
    if (!opt_duid) {
        // This should not happen. We have checked this before.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
867
868
869
870
        // see sanityCheck() called from processRelease()
        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_MISSING_CLIENTID)
            .arg(release->getRemoteAddr().toText());

871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
        reply->addOption(createStatusCode(STATUS_UnspecFail,
                         "You did not include mandatory client-id"));
        return;
    }
    DuidPtr duid(new DUID(opt_duid->getData()));

    int general_status = STATUS_Success;
    for (Option::OptionCollection::iterator opt = release->options_.begin();
         opt != release->options_.end(); ++opt) {
        switch (opt->second->getType()) {
        case D6O_IA_NA: {
            OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
            if (answer_opt) {
                reply->addOption(answer_opt);
            }
            break;
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
889
890
        // @todo: add support for IA_PD
        // @todo: add support for IA_TA
891
        default:
Tomek Mrugalski's avatar
Tomek Mrugalski committed
892
893
            // remaining options are stateless and thus ignored in this context
            ;
894
895
        }
    }
896

897
898
899
900
901
    // To be pedantic, we should also include status code in the top-level
    // scope, not just in each IA_NA. See RFC3315, section 18.2.6.
    // This behavior will likely go away in RFC3315bis.
    reply->addOption(createStatusCode(general_status,
                     "Summary status for all processed IA_NAs"));
902
903
}

904
OptionPtr
905
Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr /* question */,
906
                        int& general_status, boost::shared_ptr<Option6IA> ia) {
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
    // Release can be done in one of two ways:
    // Approach 1: extract address from client's IA_NA and see if it belongs
    // to this particular client.
    // Approach 2: find a subnet for this client, get a lease for
    // this subnet/duid/iaid and check if its content matches to what the
    // client is asking us to release.
    //
    // This method implements approach 1.

    // That's our response
    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));

    boost::shared_ptr<Option6IAAddr> release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
        (ia->getOption(D6O_IAADDR));
    if (!release_addr) {
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                                           "You did not include address in your RELEASE"));
        general_status = STATUS_NoBinding;
        return (ia_rsp);
    }

    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());

    if (!lease) {
        // client releasing a lease that we don't know about.

        // Insert status code NoAddrsAvail.
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                          "Sorry, no known leases for this duid/iaid, can't release."));
        general_status = STATUS_NoBinding;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
938
        LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
939
940
941
942
943
944
945
946
947
948
949
            .arg(duid->toText())
            .arg(ia->getIAID());

        return (ia_rsp);
    }

    if (!lease->duid_) {
        // Something is gravely wrong here. We do have a lease, but it does not
        // have mandatory DUID information attached. Someone was messing with our
        // database.

Tomek Mrugalski's avatar
Tomek Mrugalski committed
950
        LOG_ERROR(dhcp6_logger, DHCP6_LEASE_WITHOUT_DUID)
951
952
953
954
955
956
957
958
959
960
961
            .arg(release_addr->getAddress().toText());

        general_status = STATUS_UnspecFail;
        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
                          "Database consistency check failed when trying to RELEASE"));
        return (ia_rsp);
    }

    if (*duid != *(lease->duid_)) {
        // Sorry, it's not your address. You can't release it.

962
        LOG_INFO(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
963
            .arg(duid->toText())
964
            .arg(release_addr->getAddress().toText())
965
966
967
968
969
970
971
972
973
974
975
976
            .arg(lease->duid_->toText());

        general_status = STATUS_NoBinding;
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                          "This address does not belong to you, you can't release it"));
        return (ia_rsp);
    }

    if (ia->getIAID() != lease->iaid_) {
        // This address belongs to this client, but to a different IA
        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
            .arg(duid->toText())
977
            .arg(release_addr->getAddress().toText())
Tomek Mrugalski's avatar
Tomek Mrugalski committed
978
979
            .arg(lease->iaid_)
            .arg(ia->getIAID());
980
981
982
983
984
985
        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                          "This is your address, but you used wrong IAID"));
        general_status = STATUS_NoBinding;
        return (ia_rsp);
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
986
987
    // It is not necessary to check if the address matches as we used
    // getLease6(addr) method that is supposed to return a proper lease.
988

989
    // Ok, we've passed all checks. Let's release this address.
990

991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
    if (!LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
                          "Server failed to release a lease"));

        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
            .arg(lease->addr_.toText())
            .arg(duid->toText())
            .arg(lease->iaid_);
        general_status = STATUS_UnspecFail;

        return (ia_rsp);
    } else {
        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
            .arg(lease->addr_.toText())
            .arg(duid->toText())
            .arg(lease->iaid_);
1007

1008
1009
1010
1011
1012
        ia_rsp->addOption(createStatusCode(STATUS_Success,
                          "Lease released. Thank you, please come again."));

        return (ia_rsp);
    }
1013
1014
}

1015
1016
Pkt6Ptr
Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
1017

1018
1019
    sanityCheck(solicit, MANDATORY, FORBIDDEN);

1020
    Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
1021
1022
1023
1024
1025
1026
1027
1028

    copyDefaultOptions(solicit, advertise);
    appendDefaultOptions(solicit, advertise);
    appendRequestedOptions(solicit, advertise);

    assignLeases(solicit, advertise);

    return (advertise);
1029
1030
}

1031
1032
Pkt6Ptr
Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
1033
1034
1035

    sanityCheck(request, MANDATORY, MANDATORY);

1036
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
1037
1038
1039
1040
1041
1042
1043
1044

    copyDefaultOptions(request, reply);
    appendDefaultOptions(request, reply);
    appendRequestedOptions(request, reply);

    assignLeases(request, reply);

    return (reply);
1045
1046
}

1047
1048
Pkt6Ptr
Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
1049
1050
1051

    sanityCheck(renew, MANDATORY, MANDATORY);

1052
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
1053
1054
1055
1056
1057
1058
1059

    copyDefaultOptions(renew, reply);
    appendDefaultOptions(renew, reply);
    appendRequestedOptions(renew, reply);

    renewLeases(renew, reply);

1060
1061
1062
    return reply;
}

1063
1064
Pkt6Ptr
Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1065
1066
    /// @todo: Implement this
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
1067
1068
1069
    return reply;
}

1070
1071
Pkt6Ptr
Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1072
    /// @todo: Implement this
1073
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
1074
1075
1076
    return reply;
}

1077
1078
Pkt6Ptr
Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
1079
1080
1081

    sanityCheck(release, MANDATORY, MANDATORY);

1082
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
1083
1084
1085
1086
1087
1088

    copyDefaultOptions(release, reply);
    appendDefaultOptions(release, reply);

    releaseLeases(release, reply);

1089
1090
1091
    return reply;
}

1092
1093
Pkt6Ptr
Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1094
    /// @todo: Implement this
1095
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
1096
1097
    return reply;
}
1098

1099
1100
Pkt6Ptr
Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1101
    /// @todo: Implement this
1102
    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
1103
1104
    return reply;
}
1105

1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
void
Dhcpv6Srv::openActiveSockets(const uint16_t port) {
    IfaceMgr::instance().closeSockets();

    // Get the reference to the collection of interfaces. This reference should be
    // valid as long as the program is run because IfaceMgr is a singleton.
    // Therefore we can safely iterate over instances of all interfaces and modify
    // their flags. Here we modify flags which indicate wheter socket should be
    // open for a particular interface or not.
    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
         iface != ifaces.end(); ++iface) {
        Iface* iface_ptr = IfaceMgr::instance().getIface(iface->getName());
        if (CfgMgr::instance().isActiveIface(iface->getName())) {
            iface_ptr->inactive4_ = false;
            LOG_INFO(dhcp6_logger, DHCP6_ACTIVATE_INTERFACE)
                .arg(iface->getFullName());

        } else {
            // For deactivating interface, it should be sufficient to log it
            // on the debug level because it is more useful to know what
            // interface is activated which is logged on the info level.
            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC,
                      DHCP6_DEACTIVATE_INTERFACE).arg(iface->getName());
            iface_ptr->inactive6_ = true;

        }
    }
    // Let's reopen active sockets. openSockets6 will check internally whether
    // sockets are marked active or inactive.
1136
1137
    // @todo Optimization: we should not reopen all sockets but rather select
    // those that have been affected by the new configuration.
1138
1139
1140
1141
1142
    if (!IfaceMgr::instance().openSockets6(port)) {
        LOG_WARN(dhcp6_logger, DHCP6_NO_SOCKETS_OPEN);
    }
}

1143
1144
};
};