pkt4.cc 9.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
//
// 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/pkt4.h>
16
#include <dhcp/libdhcp++.h>
17 18 19 20 21 22 23 24 25 26 27
#include <dhcp/dhcp4.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
#include <iostream>
#include <sstream>

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

namespace isc {
28
namespace dhcp {
29

30 31
const IOAddress DEFAULT_ADDRESS("0.0.0.0");

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

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

    data_.resize(len);
    memcpy(&data_[0], data, len);
84 85 86 87 88 89
}

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

90
    // ... and sum of lengths of all options
91
    for (Option::OptionCollection::const_iterator it = options_.begin();
92 93 94 95 96
         it != options_.end();
         ++it) {
        length += (*it).second->len();
    }

97 98 99 100 101
    return (length);
}

bool
Pkt4::pack() {
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
    bufferOut_.writeUint8(op_);
    bufferOut_.writeUint8(htype_);
    bufferOut_.writeUint8(hlen_);
    bufferOut_.writeUint8(hops_);
    bufferOut_.writeUint32(transid_);
    bufferOut_.writeUint16(secs_);
    bufferOut_.writeUint16(flags_);
    bufferOut_.writeUint32(ciaddr_);
    bufferOut_.writeUint32(yiaddr_);
    bufferOut_.writeUint32(siaddr_);
    bufferOut_.writeUint32(giaddr_);
    bufferOut_.writeData(chaddr_, MAX_CHADDR_LEN);
    bufferOut_.writeData(sname_, MAX_SNAME_LEN);
    bufferOut_.writeData(file_, MAX_FILE_LEN);

117 118 119
    // write DHCP magic cookie
    bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE);

120
    LibDHCP::packOptions(bufferOut_, options_);
121

122 123 124 125
    // add END option that indicates end of options
    // (End option is very simple, just a 255 octet)
    bufferOut_.writeUint8(DHO_END);

126
    return (true);
127
}
Tomek Mrugalski's avatar
Tomek Mrugalski committed
128 129

void
130
Pkt4::unpack() {
131 132 133 134

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

Tomek Mrugalski's avatar
Tomek Mrugalski committed
135
    if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) {
136
        isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
137
                  << bufferIn.getLength() << " received, at least "
138 139 140
                  << DHCPV4_PKT_HDR_LEN << "is expected");
    }

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    op_ = bufferIn.readUint8();
    htype_ = bufferIn.readUint8();
    hlen_ = bufferIn.readUint8();
    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());
    bufferIn.readData(chaddr_, MAX_CHADDR_LEN);
    bufferIn.readData(sname_, MAX_SNAME_LEN);
    bufferIn.readData(file_, MAX_FILE_LEN);

156 157 158 159
    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.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
160
        isc_throw(InvalidOperation, "Recevied BOOTP packet. BOOTP is not supported.");
161 162 163 164 165 166 167 168 169 170 171 172
    }

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

173
    size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
174
    vector<uint8_t> optsBuffer;
175

Tomek Mrugalski's avatar
Tomek Mrugalski committed
176
    // First use of readVector.
177
    bufferIn.readVector(optsBuffer, opts_len);
178
    LibDHCP::unpackOptions4(optsBuffer, options_);
179

Tomek Mrugalski's avatar
Tomek Mrugalski committed
180 181
    // TODO: check will need to be called separately, so hooks can be called after
    // packet is parsed, but before its content is verified
182
    check();
183 184
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198
void Pkt4::check() {
    boost::shared_ptr<Option> typeOpt = getOption(DHO_DHCP_MESSAGE_TYPE);
    if (typeOpt) {
        uint8_t msg_type = typeOpt->getUint8();
        if (msg_type>DHCPLEASEACTIVE) {
            isc_throw(BadValue, "Invalid DHCP message type received:" << msg_type);
        }
        msg_type_ = msg_type;

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

199
void Pkt4::repack() {
200
    cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
201

202
    bufferOut_.writeData(&data_[0], data_.size());
203 204
}

205 206 207
std::string
Pkt4::toText() {
    stringstream tmp;
208 209 210 211 212 213 214 215 216 217 218
    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;
    }

219 220 221 222

    return tmp.str();
}

223
void
224 225
Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
                const std::vector<uint8_t>& macAddr) {
226 227 228
    /// TODO Rewrite this once support for client-identifier option
    /// is implemented (ticket 1228?)
    if (hlen>MAX_CHADDR_LEN) {
229
        isc_throw(OutOfRange, "Hardware address (len=" << hlen
230 231
                  << " too long. Max " << MAX_CHADDR_LEN << " supported.");
    }
232
    if ( (macAddr.size() == 0) && (hlen > 0) ) {
233 234 235
        isc_throw(OutOfRange, "Invalid HW Address specified");
    }

236 237 238
    htype_ = hType;
    hlen_ = hlen;
    memset(chaddr_, 0, MAX_CHADDR_LEN);
239
    memcpy(chaddr_, &macAddr[0], hlen);
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
}

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.");
    }
    memset(sname_, 0, MAX_SNAME_LEN);
    memcpy(sname_, sname, snameLen);

    // no need to store snameLen as any empty space is filled with 0s
}

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.");
    }
    memset(file_, 0, MAX_FILE_LEN);
    memcpy(file_, file, fileLen);

263
    // no need to store fileLen as any empty space is filled with 0s
264 265
}

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
uint8_t
Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
    switch (dhcpType) {
    case DHCPDISCOVER:
    case DHCPREQUEST:
    case DHCPDECLINE:
    case DHCPRELEASE:
    case DHCPINFORM:
    case DHCPLEASEQUERY:
        return (BOOTREQUEST);
    case DHCPACK:
    case DHCPNAK:
    case DHCPOFFER:
    case DHCPLEASEUNASSIGNED:
    case DHCPLEASEUNKNOWN:
    case DHCPLEASEACTIVE:
        return (BOOTREPLY);
    default:
        isc_throw(OutOfRange, "Invalid message type: "
                  << static_cast<int>(dhcpType) );
    }
}
288

289 290 291 292 293 294 295 296 297 298 299 300
void
Pkt4::addOption(boost::shared_ptr<Option> opt) {
    // check for uniqueness (DHCPv4 options must be unique)
    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) {
301
    Option::OptionCollection::const_iterator x = options_.find(type);
302 303 304 305 306 307
    if (x!=options_.end()) {
        return (*x).second;
    }
    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
}

308 309
void
Pkt4::updateTimestamp() {
310
    timestamp_ = boost::posix_time::microsec_clock::universal_time();
311 312
}

313 314 315
} // end of namespace isc::dhcp

} // end of namespace isc