dhcp4_srv.cc 9.21 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 42 43 44
    try {
        // first call to instance() will create IfaceMgr (it's a singleton)
        // it may throw something if things go wrong
        IfaceMgr::instance();
45

46
        /// @todo: instantiate LeaseMgr here once it is imlpemented.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
47

48
        IfaceMgr::instance().openSockets4(port);
49

50
        setServerID();
51

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

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

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

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

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
76
        // client's message and server's response
77
        Pkt4Ptr query = IfaceMgr::instance().receive4(timeout);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
78
        Pkt4Ptr rsp;
79

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

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

93

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

99 100 101
            case DHCPREQUEST:
                rsp = processRequest(query);
                break;
102

103 104 105
            case DHCPRELEASE:
                processRelease(query);
                break;
106

107 108 109
            case DHCPDECLINE:
                processDecline(query);
                break;
110

111 112 113
            case DHCPINFORM:
                processInform(query);
                break;
114

115
            default:
116 117
                LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_UNKNOWN)
                          .arg(query->getType()).arg(query->getIface());
118 119 120
            }

            if (rsp) {
121 122 123 124 125 126 127 128 129
                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);
                }

130 131 132 133 134
                rsp->setLocalAddr(query->getLocalAddr());
                rsp->setLocalPort(DHCP4_SERVER_PORT);
                rsp->setIface(query->getIface());
                rsp->setIndex(query->getIndex());

135 136 137 138 139
                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);
140
                }
141
                IfaceMgr::instance().send(rsp);
142 143 144 145 146 147 148 149 150 151 152 153
            }
        }

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

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

165

166
void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
167 168 169
    answer->setIface(question->getIface());
    answer->setIndex(question->getIndex());
    answer->setCiaddr(question->getCiaddr());
170

171 172
    answer->setSiaddr(IOAddress("0.0.0.0")); // explictly set this to 0
    answer->setHops(question->getHops());
173 174

    // copy MAC address
Tomek Mrugalski's avatar
Tomek Mrugalski committed
175 176
    vector<uint8_t> mac(question->getChaddr(),
                        question->getChaddr() + Pkt4::MAX_CHADDR_LEN);
177
    answer->setHWAddr(question->getHtype(), question->getHlen(), mac);
178 179

    // relay address
180 181 182
    answer->setGiaddr(question->getGiaddr());

    if (question->getGiaddr().toText() != "0.0.0.0") {
183
        // relayed traffic
184
        answer->setRemoteAddr(question->getGiaddr());
185 186
    } else {
        // direct traffic
187
        answer->setRemoteAddr(question->getRemoteAddr());
188 189
    }

190 191
}

192 193
void Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
    OptionPtr opt;
194 195 196

    // add Message Type Option (type 53)
    std::vector<uint8_t> tmp;
197
    tmp.push_back(static_cast<uint8_t>(msg_type));
198
    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
199 200
    msg->addOption(opt);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
201
    // DHCP Server Identifier (type 54)
202
    opt = OptionPtr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
203 204 205
        (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
    msg->addOption(opt);

206 207 208 209
    // more options will be added here later
}


210 211
void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
    OptionPtr opt;
212

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
219
    // DNS servers (type 6)
220
    opt = OptionPtr(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
221 222
    msg->addOption(opt);
}
223

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
227 228
    // TODO: Implement actual lease assignment here
    msg->setYiaddr(IOAddress(HARDCODED_LEASE));
229 230

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

    // Subnet mask (type 1)
237
    opt = OptionPtr(new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
238
    msg->addOption(opt);
239 240

    // Router (type 3)
241
    opt = OptionPtr(new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
242 243
    msg->addOption(opt);
}
244

245
Pkt4Ptr Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
246 247
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DISCOVER)
              .arg(discover->getIface());
248
    Pkt4Ptr offer = Pkt4Ptr
Tomek Mrugalski's avatar
Tomek Mrugalski committed
249
        (new Pkt4(DHCPOFFER, discover->getTransid()));
250

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
255
    tryAssignLease(offer);
256 257

    return (offer);
258 259
}

260
Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
261 262
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_REQUEST)
              .arg(request->getIface());
263
    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
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RELEASE)
              .arg(release->getIface());
278 279
    /// TODO: Implement this.
}
280

281
void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
282 283
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DECLINE)
              .arg(decline->getIface());
284 285 286
    /// TODO: Implement this.
}

287
Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
288 289
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_INFORM)
              .arg(inform->getIface());
290
    /// TODO: Currently implemented echo mode. Implement this for real
291 292
    return (inform);
}