dhcp4_srv.cc 9.6 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 = IfaceMgr::instance().receive4(timeout);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
77
        Pkt4Ptr rsp;
78

79
        if (query) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
80
81
            try {
                query->unpack();
82

Tomek Mrugalski's avatar
Tomek Mrugalski committed
83
            } catch (const std::exception& e) {
84
85
86
                // Failed to parse the packet.
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
                          DHCP4_PACKET_PARSE_FAIL).arg(e.what());
87
88
                continue;
            }
89
90
91
92
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_UNKNOWN)
                      .arg(serverReceivedPacketName(query->getType()))
                      .arg(query->getType())
                      .arg(query->getIface());
93
94
95
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUERY_DATA)
                      .arg(query->toText());

96
97
98
99
            switch (query->getType()) {
            case DHCPDISCOVER:
                rsp = processDiscover(query);
                break;
100

101
102
103
            case DHCPREQUEST:
                rsp = processRequest(query);
                break;
104

105
106
107
            case DHCPRELEASE:
                processRelease(query);
                break;
108

109
110
111
            case DHCPDECLINE:
                processDecline(query);
                break;
112

113
114
115
            case DHCPINFORM:
                processInform(query);
                break;
116

117
            default:
118
119
120
121
                // Only action is to output a message if debug is enabled,
                // and that will be covered by the debug statement before
                // the "switch" statement.
                ;
122
123
124
            }

            if (rsp) {
125
126
127
128
129
130
131
132
133
                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);
                }

134
135
136
137
138
                rsp->setLocalAddr(query->getLocalAddr());
                rsp->setLocalPort(DHCP4_SERVER_PORT);
                rsp->setIface(query->getIface());
                rsp->setIndex(query->getIndex());

139
140
141
142
143
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
                          DHCP4_RESPONSE_DATA)
                          .arg(rsp->getType()).arg(rsp->toText());
                if (!rsp->pack()) {
                    LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
144
                }
145
                IfaceMgr::instance().send(rsp);
146
147
148
149
150
151
152
153
154
155
156
157
            }
        }

        // 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() {
158
159
160
    /// TODO implement this for real once interface detection (ticket 1237)
    /// is done. Use hardcoded server-id for now.

161
#if 0
162
    // uncomment this once ticket 1350 is merged.
163
    IOAddress srvId("127.0.0.1");
164
    serverid_ = OptionPtr(
165
166
167
168
      new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
#endif
}

169

170
void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
171
172
173
    answer->setIface(question->getIface());
    answer->setIndex(question->getIndex());
    answer->setCiaddr(question->getCiaddr());
174

175
176
    answer->setSiaddr(IOAddress("0.0.0.0")); // explictly set this to 0
    answer->setHops(question->getHops());
177
178

    // copy MAC address
Tomek Mrugalski's avatar
Tomek Mrugalski committed
179
180
    vector<uint8_t> mac(question->getChaddr(),
                        question->getChaddr() + Pkt4::MAX_CHADDR_LEN);
181
    answer->setHWAddr(question->getHtype(), question->getHlen(), mac);
182
183

    // relay address
184
185
186
    answer->setGiaddr(question->getGiaddr());

    if (question->getGiaddr().toText() != "0.0.0.0") {
187
        // relayed traffic
188
        answer->setRemoteAddr(question->getGiaddr());
189
190
    } else {
        // direct traffic
191
        answer->setRemoteAddr(question->getRemoteAddr());
192
193
    }

194
195
}

196
197
void Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
    OptionPtr opt;
198
199
200

    // add Message Type Option (type 53)
    std::vector<uint8_t> tmp;
201
    tmp.push_back(static_cast<uint8_t>(msg_type));
202
    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
203
204
    msg->addOption(opt);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
205
    // DHCP Server Identifier (type 54)
206
    opt = OptionPtr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
207
208
209
        (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
    msg->addOption(opt);

210
211
212
213
    // more options will be added here later
}


214
215
void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
    OptionPtr opt;
216

Tomek Mrugalski's avatar
Tomek Mrugalski committed
217
218
    // Domain name (type 15)
    vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
219
    opt = OptionPtr(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
220
221
    msg->addOption(opt);
    // TODO: Add Option_String class
222

Tomek Mrugalski's avatar
Tomek Mrugalski committed
223
    // DNS servers (type 6)
224
    opt = OptionPtr(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
225
226
    msg->addOption(opt);
}
227

228
229
void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
    OptionPtr opt;
230

Tomek Mrugalski's avatar
Tomek Mrugalski committed
231
232
    // TODO: Implement actual lease assignment here
    msg->setYiaddr(IOAddress(HARDCODED_LEASE));
233
234

    // IP Address Lease time (type 51)
235
    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
236
    opt->setUint32(HARDCODED_LEASE_TIME);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
237
    msg->addOption(opt);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
238
    // TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
239
240

    // Subnet mask (type 1)
241
    opt = OptionPtr(new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
242
    msg->addOption(opt);
243
244

    // Router (type 3)
245
    opt = OptionPtr(new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
246
247
    msg->addOption(opt);
}
248

249
250
Pkt4Ptr Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
    Pkt4Ptr offer = Pkt4Ptr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
251
        (new Pkt4(DHCPOFFER, discover->getTransid()));
252

Tomek Mrugalski's avatar
Tomek Mrugalski committed
253
254
255
256
    copyDefaultFields(discover, offer);
    appendDefaultOptions(offer, DHCPOFFER);
    appendRequestedOptions(offer);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
257
    tryAssignLease(offer);
258
259

    return (offer);
260
261
}

262
263
Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
    Pkt4Ptr ack = Pkt4Ptr
264
265
266
267
        (new Pkt4(DHCPACK, request->getTransid()));

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
270
    tryAssignLease(ack);
271
272

    return (ack);
273
274
}

275
void Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
276
277
    /// TODO: Implement this.
}
278

279
void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
280
281
282
    /// TODO: Implement this.
}

283
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
284
    /// TODO: Currently implemented echo mode. Implement this for real
285
286
    return (inform);
}
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
312
313
314
315
316
317

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);
}