pkt_transform.cc 8.12 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
// 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 <iostream>

#include <exceptions/exceptions.h>
#include <dhcp/option.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/dhcp6.h>

#include "pkt_transform.h"
#include "localized_option.h"

using namespace std;
using namespace isc;
using namespace dhcp;

namespace isc {
namespace perfdhcp {

bool
PktTransform::pack(const Option::Universe universe,
                   const OptionBuffer& in_buffer,
                   const Option::OptionCollection& options,
                   const size_t transid_offset,
                   const uint32_t transid,
                   util::OutputBuffer& out_buffer) {

    // Always override the packet if function is called.
    out_buffer.clear();
    // Write whole buffer to output buffer.
    out_buffer.writeData(&in_buffer[0], in_buffer.size());

    uint8_t transid_len = (universe == Option::V6) ? 3 : 4;

    if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
        (transid_offset == 0)) {
        cout << "Failed to build packet: provided transaction id offset: "
             << transid_offset <<  " is out of bounds (expected 1.."
             << in_buffer.size()-1 << ")." << endl;
        return (false);
    }

    // Seek to the transaction id position in output buffer.
    out_buffer.clear();
    out_buffer.skip(transid_offset);

    try {
60
        if (universe == Option::V4) {
61 62 63 64 65 66 67 68 69 70 71 72 73 74
            out_buffer.writeUint8(transid >> 24 & 0xFF);
        }
        out_buffer.writeUint8(transid >> 16 & 0xFF);
        out_buffer.writeUint8(transid >> 8 & 0xFF);
        out_buffer.writeUint8(transid & 0xFF);

        // Buffer pointer is at the end of transaction id.
        // We have to seek to the end of buffer so as data don't
        // get truncated.
        out_buffer.skip(in_buffer.size() - out_buffer.getLength());

        // We already have packet template stored in output buffer
        // but still some options have to be updated if client
        // specified them along with their offsets in the buffer.
75
        PktTransform::packOptions(in_buffer, options, out_buffer);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    } catch (const isc::BadValue& e) {
        cout << "Building packet failed: " << e.what() << endl;
        return (false);
    }
    return (true);
}

bool
PktTransform::unpack(const Option::Universe universe,
                     const OptionBuffer& in_buffer,
                     const Option::OptionCollection& options,
                     const size_t transid_offset,
                     uint32_t& transid) {

    uint8_t transid_len = (universe == Option::V6) ? 3 : 4;

    // Validate transaction id offset.
    if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
        (transid_offset == 0)) {
        cout << "Failed to parse packet: provided transaction id offset: "
             << transid_offset <<  " is out of bounds (expected 1.."
             << in_buffer.size()-1 << ")." << endl;
        return (false);
    }

    if (universe == Option::V6) {
        transid = ((in_buffer[transid_offset] << 16) +
                   (in_buffer[transid_offset + 1] << 8) +
                   (in_buffer[transid_offset + 2]))
            & 0xFFFFFF;
    } else {
        transid = ((in_buffer[transid_offset] << 24) +
108
                   (in_buffer[transid_offset + 1] << 16) +
109 110 111 112 113
                   (in_buffer[transid_offset + 2] << 8) +
                   (in_buffer[transid_offset + 3]));
    }

    try {
114
        PktTransform::unpackOptions(in_buffer, options);
115 116 117 118 119 120 121 122 123 124
    } catch (const isc::BadValue& e) {
        cout << "Packet parsing failed: " << e.what() << endl;
        return (false);
    }

    return (true);
}

void
PktTransform::packOptions(const OptionBuffer& in_buffer,
125 126
                          const Option::OptionCollection& options,
                          util::OutputBuffer& out_buffer) {
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    try {
        // If there are any options on the list, we will use provided
        // options offsets to override them in the output buffer
        // with new contents.
        for (Option::OptionCollection::const_iterator it = options.begin();
             it != options.end(); ++it) {
            // Get options with their position (offset).
            boost::shared_ptr<LocalizedOption> option =
                boost::dynamic_pointer_cast<LocalizedOption>(it->second);
            uint32_t offset = option->getOffset();
            if ((offset == 0) ||
                (offset + option->len() > in_buffer.size())) {
                isc_throw(isc::BadValue,
                          "option offset for option: " << option->getType()
                          << " is out of bounds (expected 1.."
                          << in_buffer.size() - option->len() << ")");
            }
            out_buffer.clear();
            out_buffer.skip(offset);

            // Replace existing option with new value.
            option->pack(out_buffer);
        }
        // Seek to the end of the buffer to make sure its size is correct.
        out_buffer.skip(in_buffer.size() - out_buffer.getLength());
    }
    catch (const Exception&) {
        isc_throw(isc::BadValue, "failed to pack options into buffer.");
    }
}

void
159
PktTransform::unpackOptions(const OptionBuffer& in_buffer,
160 161 162 163 164 165 166 167 168 169
                            const Option::OptionCollection& options) {
    for (Option::OptionCollection::const_iterator it = options.begin();
         it != options.end(); ++it) {

        boost::shared_ptr<LocalizedOption> option =
            boost::dynamic_pointer_cast<LocalizedOption>(it->second);
        size_t opt_pos = option->getOffset();
        if (opt_pos == 0) {
            isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
                      "(Option position not specified)");
170
        } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
171 172 173 174 175 176 177 178
            isc_throw(isc::BadValue,
                      "failed to unpack options from from raw buffer "
                      "(Option position out of bounds)");
        }

        size_t offset = opt_pos;
        size_t offset_step = 1;
        uint16_t opt_type = 0;
179
        if (option->getUniverse() == Option::V6) {
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
            offset_step = 2;
            // For DHCPv6 option type is in first two octets.
            opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
        } else {
            // For DHCPv4 option type is in first octet.
            opt_type = in_buffer[offset];
        }
        // Check if we got expected option type.
        if (opt_type != option->getType()) {
            isc_throw(isc::BadValue,
                      "failed to unpack option from raw buffer "
                      "(option type mismatch)");
        }

        // Get option length which is supposed to be after option type.
        offset += offset_step;
        uint16_t opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
197
        if (option->getUniverse() == Option::V6) {
198 199 200 201
            opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
        } else {
            opt_len = in_buffer[offset];
        }
202

203
        // Check if packet is not truncated.
204
        if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
            isc_throw(isc::BadValue,
                      "failed to unpack option from raw buffer "
                      "(option truncated)");
        }

        // Seek to actual option data and replace it.
        offset += offset_step;
        option->setData(in_buffer.begin() + offset,
                        in_buffer.begin() + offset + opt_len);
    }
}


} // namespace perfdhcp
} // namespace isc