dhcp4_srv.cc 9.98 KB
Newer Older
1
// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
// 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.

#include <dhcp/dhcp4.h>
#include <dhcp/pkt4.h>
17
#include <dhcp/iface_mgr.h>
18
#include <dhcp4/dhcp4_srv.h>
19
#include <dhcp4/dhcp4_log.h>
20
#include <asiolink/io_address.h>
21
#include <dhcp/option4_addrlst.h>
22
23
24

using namespace isc;
using namespace isc::asiolink;
25
26
27
using namespace isc::dhcp;
using namespace isc::log;
using namespace std;
28

29
30
// These are hardcoded parameters. Currently this is a skeleton server that only
// grants those options and a single, fixed, hardcoded lease.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
31
const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
32
33
const std::string HARDCODED_NETMASK = "255.255.255.0";
const uint32_t    HARDCODED_LEASE_TIME = 60; // in seconds
Tomek Mrugalski's avatar
Tomek Mrugalski committed
34
35
36
37
const std::string HARDCODED_GATEWAY = "192.0.2.1";
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
const std::string HARDCODED_SERVER_ID = "192.0.2.1";
38

39
Dhcpv4Srv::Dhcpv4Srv(uint16_t port) {
40
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
41
    try {
42
        // First call to instance() will create IfaceMgr (it's a singleton)
43
44
        // it may throw something if things go wrong
        IfaceMgr::instance();
45

46
47
        /// @todo: instantiate LeaseMgr here once it is imlpemented.
        IfaceMgr::instance().openSockets4(port);
48

49
        setServerID();
50

51
    } catch (const std::exception &e) {
52
        LOG_ERROR(dhcp4_logger, DHCP4_SRV_CONSTRUCT_ERROR).arg(e.what());
53
54
55
        shutdown_ = true;
        return;
    }
56

Tomek Mrugalski's avatar
Tomek Mrugalski committed
57
    shutdown_ = false;
58
59
60
}

Dhcpv4Srv::~Dhcpv4Srv() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
61
    IfaceMgr::instance().closeSockets();
62
63
}

64
void Dhcpv4Srv::shutdown() {
65
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_SHUTDOWN_REQUEST);
66
67
68
    shutdown_ = true;
}

69
70
bool
Dhcpv4Srv::run() {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
71
    while (!shutdown_) {
72
73
74
        /// @todo: calculate actual timeout once we have lease database
        int timeout = 1000;

Tomek Mrugalski's avatar
Tomek Mrugalski committed
75
        // client's message and server's response
76
        Pkt4Ptr query;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
77
        Pkt4Ptr rsp;
78

79
80
81
82
83
84
        try {
            query = IfaceMgr::instance().receive4(timeout);
        } catch (const std::exception& e) {
            LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
        }

85
        if (query) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
86
87
            try {
                query->unpack();
88

Tomek Mrugalski's avatar
Tomek Mrugalski committed
89
            } catch (const std::exception& e) {
90
91
92
                // Failed to parse the packet.
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
                          DHCP4_PACKET_PARSE_FAIL).arg(e.what());
93
94
                continue;
            }
95
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
96
97
98
                      .arg(serverReceivedPacketName(query->getType()))
                      .arg(query->getType())
                      .arg(query->getIface());
99
100
101
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
                      .arg(query->toText());

102
103
104
105
            switch (query->getType()) {
            case DHCPDISCOVER:
                rsp = processDiscover(query);
                break;
106

107
108
109
            case DHCPREQUEST:
                rsp = processRequest(query);
                break;
110

111
112
113
            case DHCPRELEASE:
                processRelease(query);
                break;
114

115
116
117
            case DHCPDECLINE:
                processDecline(query);
                break;
118

119
120
121
            case DHCPINFORM:
                processInform(query);
                break;
122

123
            default:
124
125
126
127
                // Only action is to output a message if debug is enabled,
                // and that will be covered by the debug statement before
                // the "switch" statement.
                ;
128
129
130
            }

            if (rsp) {
131
132
133
134
135
136
137
138
139
                if (rsp->getRemoteAddr().toText() == "0.0.0.0") {
                    rsp->setRemoteAddr(query->getRemoteAddr());
                }
                if (!rsp->getHops()) {
                    rsp->setRemotePort(DHCP4_CLIENT_PORT);
                } else {
                    rsp->setRemotePort(DHCP4_SERVER_PORT);
                }

140
141
142
143
144
                rsp->setLocalAddr(query->getLocalAddr());
                rsp->setLocalPort(DHCP4_SERVER_PORT);
                rsp->setIface(query->getIface());
                rsp->setIndex(query->getIndex());

145
146
147
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
                          DHCP4_RESPONSE_DATA)
                          .arg(rsp->getType()).arg(rsp->toText());
148
149

                if (rsp->pack()) {
150
151
                    try {
                        IfaceMgr::instance().send(rsp);
Marcin Siodelski's avatar
Marcin Siodelski committed
152
153
                    } catch (const std::exception& e) {
                        LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL).arg(e.what());
154
                    }
155
                } else {
156
                    LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
157
158
159
160
161
162
163
164
165
166
167
168
169
                }
            }
        }

        // TODO add support for config session (see src/bin/auth/main.cc)
        //      so this daemon can be controlled from bob
    }

    return (true);
}

void
Dhcpv4Srv::setServerID() {
170
171
172
    /// TODO implement this for real once interface detection (ticket 1237)
    /// is done. Use hardcoded server-id for now.

173
#if 0
174
    // uncomment this once ticket 1350 is merged.
175
    IOAddress srvId("127.0.0.1");
176
    serverid_ = OptionPtr(
177
178
179
180
      new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
#endif
}

181

182
void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
183
184
185
    answer->setIface(question->getIface());
    answer->setIndex(question->getIndex());
    answer->setCiaddr(question->getCiaddr());
186

187
188
    answer->setSiaddr(IOAddress("0.0.0.0")); // explictly set this to 0
    answer->setHops(question->getHops());
189
190

    // copy MAC address
Tomek Mrugalski's avatar
Tomek Mrugalski committed
191
192
    vector<uint8_t> mac(question->getChaddr(),
                        question->getChaddr() + Pkt4::MAX_CHADDR_LEN);
193
    answer->setHWAddr(question->getHtype(), question->getHlen(), mac);
194
195

    // relay address
196
197
198
    answer->setGiaddr(question->getGiaddr());

    if (question->getGiaddr().toText() != "0.0.0.0") {
199
        // relayed traffic
200
        answer->setRemoteAddr(question->getGiaddr());
201
202
    } else {
        // direct traffic
203
        answer->setRemoteAddr(question->getRemoteAddr());
204
205
    }

206
207
}

208
209
void Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
    OptionPtr opt;
210
211
212

    // add Message Type Option (type 53)
    std::vector<uint8_t> tmp;
213
    tmp.push_back(static_cast<uint8_t>(msg_type));
214
    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
215
216
    msg->addOption(opt);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
217
    // DHCP Server Identifier (type 54)
218
    opt = OptionPtr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
219
220
221
        (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
    msg->addOption(opt);

222
223
224
225
    // more options will be added here later
}


226
227
void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
    OptionPtr opt;
228

Tomek Mrugalski's avatar
Tomek Mrugalski committed
229
230
    // Domain name (type 15)
    vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
231
    opt = OptionPtr(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
232
233
    msg->addOption(opt);
    // TODO: Add Option_String class
234

Tomek Mrugalski's avatar
Tomek Mrugalski committed
235
    // DNS servers (type 6)
236
    opt = OptionPtr(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
237
238
    msg->addOption(opt);
}
239

240
241
void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
    OptionPtr opt;
242

Tomek Mrugalski's avatar
Tomek Mrugalski committed
243
244
    // TODO: Implement actual lease assignment here
    msg->setYiaddr(IOAddress(HARDCODED_LEASE));
245
246

    // IP Address Lease time (type 51)
247
    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
248
    opt->setUint32(HARDCODED_LEASE_TIME);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
249
    msg->addOption(opt);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
250
    // TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
251
252

    // Subnet mask (type 1)
253
    opt = OptionPtr(new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
254
    msg->addOption(opt);
255
256

    // Router (type 3)
257
    opt = OptionPtr(new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
258
259
    msg->addOption(opt);
}
260

261
262
Pkt4Ptr Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
    Pkt4Ptr offer = Pkt4Ptr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
263
        (new Pkt4(DHCPOFFER, discover->getTransid()));
264

Tomek Mrugalski's avatar
Tomek Mrugalski committed
265
266
267
268
    copyDefaultFields(discover, offer);
    appendDefaultOptions(offer, DHCPOFFER);
    appendRequestedOptions(offer);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
269
    tryAssignLease(offer);
270
271

    return (offer);
272
273
}

274
275
Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
    Pkt4Ptr ack = Pkt4Ptr
276
277
278
279
        (new Pkt4(DHCPACK, request->getTransid()));

    copyDefaultFields(request, ack);
    appendDefaultOptions(ack, DHCPACK);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
280
    appendRequestedOptions(ack);
281

Tomek Mrugalski's avatar
Tomek Mrugalski committed
282
    tryAssignLease(ack);
283
284

    return (ack);
285
286
}

287
void Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
288
289
    /// TODO: Implement this.
}
290

291
void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
292
293
294
    /// TODO: Implement this.
}

295
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
296
    /// TODO: Currently implemented echo mode. Implement this for real
297
298
    return (inform);
}
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

const char*
Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
    static const char* DISCOVER = "DISCOVER";
    static const char* REQUEST = "REQUEST";
    static const char* RELEASE = "RELEASE";
    static const char* DECLINE = "DECLINE";
    static const char* INFORM = "INFORM";
    static const char* UNKNOWN = "UNKNOWN";

    switch (type) {
    case DHCPDISCOVER:
        return (DISCOVER);

    case DHCPREQUEST:
        return (REQUEST);

    case DHCPRELEASE:
        return (RELEASE);

    case DHCPDECLINE:
        return (DECLINE);

    case DHCPINFORM:
        return (INFORM);

    default:
        ;
    }
    return (UNKNOWN);
}