dhcp4_srv.cc 9.84 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
//
// 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
#include <dhcp/iface_mgr.h>
18
#include <dhcp/option4_addrlst.h>
19 20 21
#include <dhcp/pkt4.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.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
191
    answer->setHWAddr(question->getHWAddr());
192 193

    // relay address
194 195 196
    answer->setGiaddr(question->getGiaddr());

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

204 205
}

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

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

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

220 221 222 223
    // more options will be added here later
}


224 225
void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
    OptionPtr opt;
226

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

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

238 239
void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
    OptionPtr opt;
240

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

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

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

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

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
267
    tryAssignLease(offer);
268 269

    return (offer);
270 271
}

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
280
    tryAssignLease(ack);
281 282

    return (ack);
283 284
}

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

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

293
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
294
    /// TODO: Currently implemented echo mode. Implement this for real
295 296
    return (inform);
}
297 298 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

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