pkt4.cc 7.71 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// 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>
#include <dhcp/libdhcp.h>
#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 59
      bufferOut_(DHCPV4_PKT_HDR_LEN),
      msg_type_(msg_type)
{
    /// TODO: fixed fields, uncomment in ticket #1224
    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)
60 61
     :local_addr_(DEFAULT_ADDRESS),
      remote_addr_(DEFAULT_ADDRESS),
62 63 64 65 66 67
      iface_(""),
      ifindex_(-1),
      local_port_(DHCP4_SERVER_PORT),
      remote_port_(DHCP4_CLIENT_PORT),
      /// TODO Fixed fields, uncomment in ticket #1224
      op_(BOOTREQUEST),
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 85

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

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

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

99 100 101 102 103
    return (length);
}

bool
Pkt4::pack() {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    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);

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

121
    return (true);
122 123 124
}
bool
Pkt4::unpack() {
125 126 127 128 129

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

    if (bufferIn.getLength()<DHCPV4_PKT_HDR_LEN) {
130
        isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len="
131
                  << bufferIn.getLength() << " received, at least "
132 133 134
                  << DHCPV4_PKT_HDR_LEN << "is expected");
    }

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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);

    size_t opts_len = bufferIn.getLength() - bufferIn.getPosition();
151 152
    vector<uint8_t> optsBuffer;
    // fist use of readVector
153
    bufferIn.readVector(optsBuffer, opts_len);
154
    LibDHCP::unpackOptions4(optsBuffer, options_);
155

156
    return (true);
157 158 159 160 161 162 163 164
}

std::string
Pkt4::toText() {
    stringstream tmp;
    tmp << "localAddr=[" << local_addr_.toText() << "]:" << local_port_
        << " remoteAddr=[" << remote_addr_.toText()
        << "]:" << remote_port_ << endl;
165 166
    tmp << "msgtype=" << msg_type_
        << ", transid=0x" << hex << transid_ << dec
167 168 169 170 171
        << endl;

    return tmp.str();
}

172
void
173 174
Pkt4::setHWAddr(uint8_t hType, uint8_t hlen,
                const std::vector<uint8_t>& macAddr) {
175 176 177
    /// TODO Rewrite this once support for client-identifier option
    /// is implemented (ticket 1228?)
    if (hlen>MAX_CHADDR_LEN) {
178
        isc_throw(OutOfRange, "Hardware address (len=" << hlen
179 180
                  << " too long. Max " << MAX_CHADDR_LEN << " supported.");
    }
181
    if ( (macAddr.size() == 0) && (hlen > 0) ) {
182 183 184
        isc_throw(OutOfRange, "Invalid HW Address specified");
    }

185 186 187
    htype_ = hType;
    hlen_ = hlen;
    memset(chaddr_, 0, MAX_CHADDR_LEN);
188
    memcpy(chaddr_, &macAddr[0], hlen);
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
}

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

212
    // no need to store fileLen as any empty space is filled with 0s
213 214
}

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
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) );
    }
}
237

238 239 240 241 242 243 244 245 246 247 248 249
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) {
250
    Option::OptionCollection::const_iterator x = options_.find(type);
251 252 253 254 255 256 257
    if (x!=options_.end()) {
        return (*x).second;
    }
    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
}


258 259 260
} // end of namespace isc::dhcp

} // end of namespace isc