dhcp4_srv.cc 9.63 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
            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
90 91 92
                      .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
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA,
                          DHCP4_RESPONSE_DATA)
                          .arg(rsp->getType()).arg(rsp->toText());
142 143 144 145

                if (rsp->pack()) {
                    IfaceMgr::instance().send(rsp);
                } else {
146
                    LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
147 148 149 150 151 152 153 154 155 156 157 158 159
                }
            }
        }

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

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

171

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

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

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

    // relay address
186 187 188
    answer->setGiaddr(question->getGiaddr());

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

196 197
}

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

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

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

212 213 214 215
    // more options will be added here later
}


216 217
void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
    OptionPtr opt;
218

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

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

230 231
void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
    OptionPtr opt;
232

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

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

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

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

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
259
    tryAssignLease(offer);
260 261

    return (offer);
262 263
}

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
272
    tryAssignLease(ack);
273 274

    return (ack);
275 276
}

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

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

285
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
286
    /// TODO: Currently implemented echo mode. Implement this for real
287 288
    return (inform);
}
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 318 319

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