pkt4.cc 11.1 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/libdhcp++.h>
18
#include <dhcp/option_int.h>
19
#include <dhcp/pkt4.h>
20
#include <exceptions/exceptions.h>
21

22
#include <algorithm>
23 24 25 26 27 28 29 30
#include <iostream>
#include <sstream>

using namespace std;
using namespace isc::dhcp;
using namespace isc::asiolink;

namespace isc {
31
namespace dhcp {
32

33 34
const IOAddress DEFAULT_ADDRESS("0.0.0.0");

35
Pkt4::Pkt4(uint8_t msg_type, uint32_t transid)
36 37
     :local_addr_(DEFAULT_ADDRESS),
      remote_addr_(DEFAULT_ADDRESS),
38 39 40 41
      iface_(""),
      ifindex_(0),
      local_port_(DHCP4_SERVER_PORT),
      remote_port_(DHCP4_CLIENT_PORT),
42
      op_(DHCPTypeToBootpType(msg_type)),
43
      hwaddr_(new HWAddr()),
44 45 46 47
      hops_(0),
      transid_(transid),
      secs_(0),
      flags_(0),
48 49 50 51
      ciaddr_(DEFAULT_ADDRESS),
      yiaddr_(DEFAULT_ADDRESS),
      siaddr_(DEFAULT_ADDRESS),
      giaddr_(DEFAULT_ADDRESS),
52 53 54 55 56 57 58 59
      bufferOut_(DHCPV4_PKT_HDR_LEN),
      msg_type_(msg_type)
{
    memset(sname_, 0, MAX_SNAME_LEN);
    memset(file_, 0, MAX_FILE_LEN);
}

Pkt4::Pkt4(const uint8_t* data, size_t len)
60 61
     :local_addr_(DEFAULT_ADDRESS),
      remote_addr_(DEFAULT_ADDRESS),
62
      iface_(""),
63
      ifindex_(0),
64 65 66
      local_port_(DHCP4_SERVER_PORT),
      remote_port_(DHCP4_CLIENT_PORT),
      op_(BOOTREQUEST),
67
      hwaddr_(new HWAddr()),
68
      transid_(0),
69 70
      secs_(0),
      flags_(0),
71 72 73 74
      ciaddr_(DEFAULT_ADDRESS),
      yiaddr_(DEFAULT_ADDRESS),
      siaddr_(DEFAULT_ADDRESS),
      giaddr_(DEFAULT_ADDRESS),
75
      bufferOut_(0), // not used, this is RX packet
76 77
      msg_type_(DHCPDISCOVER)
{
78 79
    if (len < DHCPV4_PKT_HDR_LEN) {
        isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len
80 81
                  << ") received, at least " << DHCPV4_PKT_HDR_LEN
                  << " is expected.");
82 83 84

    } else if (data == NULL) {
        isc_throw(InvalidParameter, "data buffer passed to Pkt4 is NULL");
85
    }
86 87 88

    data_.resize(len);
    memcpy(&data_[0], data, len);
89 90 91 92 93 94
}

size_t
Pkt4::len() {
    size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header

95
    // ... and sum of lengths of all options
96
    for (Option::OptionCollection::const_iterator it = options_.begin();
97 98 99 100 101
         it != options_.end();
         ++it) {
        length += (*it).second->len();
    }

102 103 104 105 106
    return (length);
}

bool
Pkt4::pack() {
107 108 109 110 111 112
    if (!hwaddr_) {
        isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set.");
    }

    size_t hw_len = hwaddr_->hwaddr_.size();

113
    bufferOut_.writeUint8(op_);
114 115
    bufferOut_.writeUint8(hwaddr_->htype_);
    bufferOut_.writeUint8(hw_len < 16 ? hw_len : 16);
116 117 118 119 120 121 122 123
    bufferOut_.writeUint8(hops_);
    bufferOut_.writeUint32(transid_);
    bufferOut_.writeUint16(secs_);
    bufferOut_.writeUint16(flags_);
    bufferOut_.writeUint32(ciaddr_);
    bufferOut_.writeUint32(yiaddr_);
    bufferOut_.writeUint32(siaddr_);
    bufferOut_.writeUint32(giaddr_);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139


    if (hw_len <=16) {
        // write up to 16 bytes of the hardware address (CHADDR field is 16
        // bytes long in DHCPv4 message).
        bufferOut_.writeData(&hwaddr_->hwaddr_[0], (hw_len<16?hw_len:16) );
        hw_len = 16 - hw_len;
    } else {
        hw_len = 16;
    }

    // write (len) bytes of padding
    vector<uint8_t> zeros(hw_len, 0);
    bufferOut_.writeData(&zeros[0], hw_len);
    // bufferOut_.writeData(chaddr_, MAX_CHADDR_LEN);

140 141 142
    bufferOut_.writeData(sname_, MAX_SNAME_LEN);
    bufferOut_.writeData(file_, MAX_FILE_LEN);

143 144 145
    // write DHCP magic cookie
    bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE);

146
    LibDHCP::packOptions(bufferOut_, options_);
147

148 149 150 151
    // add END option that indicates end of options
    // (End option is very simple, just a 255 octet)
    bufferOut_.writeUint8(DHO_END);

152
    return (true);
153
}
Tomek Mrugalski's avatar
Tomek Mrugalski committed
154 155

void
156
Pkt4::unpack() {
157 158 159 160

    // input buffer (used during message reception)
    isc::util::InputBuffer bufferIn(&data_[0], data_.size());

Tomek Mrugalski's avatar
Tomek Mrugalski committed
161
    if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) {
162
        isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
163
                  << bufferIn.getLength() << " received, at least "
164 165 166
                  << DHCPV4_PKT_HDR_LEN << "is expected");
    }

167
    op_ = bufferIn.readUint8();
168 169
    uint8_t htype = bufferIn.readUint8();
    uint8_t hlen = bufferIn.readUint8();
170 171 172 173 174 175 176 177
    hops_ = bufferIn.readUint8();
    transid_ = bufferIn.readUint32();
    secs_ = bufferIn.readUint16();
    flags_ = bufferIn.readUint16();
    ciaddr_ = IOAddress(bufferIn.readUint32());
    yiaddr_ = IOAddress(bufferIn.readUint32());
    siaddr_ = IOAddress(bufferIn.readUint32());
    giaddr_ = IOAddress(bufferIn.readUint32());
178 179 180

    vector<uint8_t> hw_addr(MAX_CHADDR_LEN, 0);
    bufferIn.readVector(hw_addr, MAX_CHADDR_LEN);
181 182 183
    bufferIn.readData(sname_, MAX_SNAME_LEN);
    bufferIn.readData(file_, MAX_FILE_LEN);

184 185 186 187
    hw_addr.resize(hlen);

    hwaddr_ = HWAddrPtr(new HWAddr(hw_addr, htype));

188 189 190 191
    if (bufferIn.getLength() == bufferIn.getPosition()) {
        // this is *NOT* DHCP packet. It does not have any DHCPv4 options. In
        // particular, it does not have magic cookie, a 4 byte sequence that
        // differentiates between DHCP and BOOTP packets.
192
        isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported.");
193 194 195 196 197 198 199 200 201 202 203 204
    }

    if (bufferIn.getLength() - bufferIn.getPosition() < 4) {
      // there is not enough data to hold magic DHCP cookie
      isc_throw(Unexpected, "Truncated or no DHCP packet.");
    }

    uint32_t magic = bufferIn.readUint32();
    if (magic != DHCP_OPTIONS_COOKIE) {
      isc_throw(Unexpected, "Invalid or missing DHCP magic cookie");
    }

205
    size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
206
    vector<uint8_t> optsBuffer;
207

Tomek Mrugalski's avatar
Tomek Mrugalski committed
208
    // First use of readVector.
209
    bufferIn.readVector(optsBuffer, opts_len);
210
    LibDHCP::unpackOptions4(optsBuffer, options_);
211

212 213
    // @todo check will need to be called separately, so hooks can be called
    // after the packet is parsed, but before its content is verified
214
    check();
215 216
}

217
void Pkt4::check() {
218 219
    boost::shared_ptr<OptionInt<uint8_t> > typeOpt =
        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(getOption(DHO_DHCP_MESSAGE_TYPE));
220
    if (typeOpt) {
221
        uint8_t msg_type = typeOpt->getValue();
222 223 224
        if (msg_type > DHCPLEASEACTIVE) {
            isc_throw(BadValue, "Invalid DHCP message type received: "
                      << msg_type);
225 226 227 228 229 230 231 232
        }
        msg_type_ = msg_type;

    } else {
        isc_throw(Unexpected, "Missing DHCP Message Type option");
    }
}

233
void Pkt4::repack() {
234
    bufferOut_.writeData(&data_[0], data_.size());
235 236
}

237 238 239
std::string
Pkt4::toText() {
    stringstream tmp;
240 241 242 243 244 245 246 247 248 249 250
    tmp << "localAddr=" << local_addr_.toText() << ":" << local_port_
        << " remoteAddr=" << remote_addr_.toText()
        << ":" << remote_port_ << ", msgtype=" << int(msg_type_)
        << ", transid=0x" << hex << transid_ << dec << endl;

    for (isc::dhcp::Option::OptionCollection::iterator opt=options_.begin();
         opt != options_.end();
         ++opt) {
        tmp << "  " << opt->second->toText() << std::endl;
    }

251 252 253 254

    return tmp.str();
}

255
void
256
Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
257 258
                const std::vector<uint8_t>& mac_addr) {
    /// @todo Rewrite this once support for client-identifier option
259
    /// is implemented (ticket 1228?)
260
    if (hlen > MAX_CHADDR_LEN) {
261
        isc_throw(OutOfRange, "Hardware address (len=" << hlen
262
                  << " too long. Max " << MAX_CHADDR_LEN << " supported.");
263 264

    } else if (mac_addr.empty() && (hlen > 0) ) {
265 266 267
        isc_throw(OutOfRange, "Invalid HW Address specified");
    }

268
    hwaddr_ = HWAddrPtr(new HWAddr(mac_addr, hType));
269 270
}

271 272 273 274 275 276 277 278
void
Pkt4::setHWAddr(const HWAddrPtr& addr) {
    if (!addr) {
        isc_throw(BadValue, "Setting hw address to NULL is forbidden");
    }
    hwaddr_ = addr;
}

279 280 281 282 283
void
Pkt4::setSname(const uint8_t* sname, size_t snameLen /*= MAX_SNAME_LEN*/) {
    if (snameLen > MAX_SNAME_LEN) {
        isc_throw(OutOfRange, "sname field (len=" << snameLen
                  << ") too long, Max " << MAX_SNAME_LEN << " supported.");
284 285 286

    } else if (sname == NULL) {
        isc_throw(InvalidParameter, "Invalid sname specified");
287
    }
288

289 290
    std::copy(&sname[0], &sname[snameLen], &sname_[0]);
    std::fill(&sname_[snameLen], &sname_[MAX_SNAME_LEN], 0);
291

292
    // No need to store snameLen as any empty space is filled with 0s
293 294 295 296 297 298 299
}

void
Pkt4::setFile(const uint8_t* file, size_t fileLen /*= MAX_FILE_LEN*/) {
    if (fileLen > MAX_FILE_LEN) {
        isc_throw(OutOfRange, "file field (len=" << fileLen
                  << ") too long, Max " << MAX_FILE_LEN << " supported.");
300 301 302

    } else if (file == NULL) {
        isc_throw(InvalidParameter, "Invalid file name specified");
303
    }
304

305 306
    std::copy(&file[0], &file[fileLen], &file_[0]);
    std::fill(&file_[fileLen], &file_[MAX_FILE_LEN], 0);
307

308
    // No need to store fileLen as any empty space is filled with 0s
309 310
}

311 312 313 314 315 316 317 318 319 320
uint8_t
Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
    switch (dhcpType) {
    case DHCPDISCOVER:
    case DHCPREQUEST:
    case DHCPDECLINE:
    case DHCPRELEASE:
    case DHCPINFORM:
    case DHCPLEASEQUERY:
        return (BOOTREQUEST);
321

322 323 324 325 326 327 328
    case DHCPACK:
    case DHCPNAK:
    case DHCPOFFER:
    case DHCPLEASEUNASSIGNED:
    case DHCPLEASEUNKNOWN:
    case DHCPLEASEACTIVE:
        return (BOOTREPLY);
329

330 331 332 333 334
    default:
        isc_throw(OutOfRange, "Invalid message type: "
                  << static_cast<int>(dhcpType) );
    }
}
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
uint8_t
Pkt4::getHtype() const {
    if (!hwaddr_) {
        isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
    }
    return (hwaddr_->htype_);
}

uint8_t
Pkt4::getHlen() const {
    if (!hwaddr_) {
        isc_throw(InvalidOperation, "Can't get HType. HWAddr not defined");
    }
    uint8_t len = hwaddr_->hwaddr_.size();
    return (len <= 16 ? len : 16);
}

353 354
void
Pkt4::addOption(boost::shared_ptr<Option> opt) {
355
    // Check for uniqueness (DHCPv4 options must be unique)
356 357 358 359 360 361 362 363 364
    if (getOption(opt->getType())) {
        isc_throw(BadValue, "Option " << opt->getType()
                  << " already present in this message.");
    }
    options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
}

boost::shared_ptr<isc::dhcp::Option>
Pkt4::getOption(uint8_t type) {
365
    Option::OptionCollection::const_iterator x = options_.find(type);
366
    if (x != options_.end()) {
367 368 369 370 371
        return (*x).second;
    }
    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
}

372 373
void
Pkt4::updateTimestamp() {
374
    timestamp_ = boost::posix_time::microsec_clock::universal_time();
375 376
}

377 378 379
} // end of namespace isc::dhcp

} // end of namespace isc