// Copyright (C) 2011-2012 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 #include #include #include #include #include #include #include #include using namespace std; using namespace isc::dhcp; using namespace isc::asiolink; namespace isc { namespace dhcp { const IOAddress DEFAULT_ADDRESS("0.0.0.0"); Pkt4::Pkt4(uint8_t msg_type, uint32_t transid) :local_addr_(DEFAULT_ADDRESS), remote_addr_(DEFAULT_ADDRESS), iface_(""), ifindex_(0), local_port_(DHCP4_SERVER_PORT), remote_port_(DHCP4_CLIENT_PORT), op_(DHCPTypeToBootpType(msg_type)), hwaddr_(new HWAddr()), hops_(0), transid_(transid), secs_(0), flags_(0), ciaddr_(DEFAULT_ADDRESS), yiaddr_(DEFAULT_ADDRESS), siaddr_(DEFAULT_ADDRESS), giaddr_(DEFAULT_ADDRESS), bufferOut_(DHCPV4_PKT_HDR_LEN) { memset(sname_, 0, MAX_SNAME_LEN); memset(file_, 0, MAX_FILE_LEN); setType(msg_type); } Pkt4::Pkt4(const uint8_t* data, size_t len) :local_addr_(DEFAULT_ADDRESS), remote_addr_(DEFAULT_ADDRESS), iface_(""), ifindex_(0), local_port_(DHCP4_SERVER_PORT), remote_port_(DHCP4_CLIENT_PORT), op_(BOOTREQUEST), hwaddr_(new HWAddr()), transid_(0), secs_(0), flags_(0), ciaddr_(DEFAULT_ADDRESS), yiaddr_(DEFAULT_ADDRESS), siaddr_(DEFAULT_ADDRESS), giaddr_(DEFAULT_ADDRESS), bufferOut_(0) // not used, this is RX packet { if (len < DHCPV4_PKT_HDR_LEN) { isc_throw(OutOfRange, "Truncated DHCPv4 packet (len=" << len << ") received, at least " << DHCPV4_PKT_HDR_LEN << " is expected."); } else if (data == NULL) { isc_throw(InvalidParameter, "data buffer passed to Pkt4 is NULL"); } data_.resize(len); memcpy(&data_[0], data, len); } size_t Pkt4::len() { size_t length = DHCPV4_PKT_HDR_LEN; // DHCPv4 header // ... and sum of lengths of all options for (Option::OptionCollection::const_iterator it = options_.begin(); it != options_.end(); ++it) { length += (*it).second->len(); } return (length); } bool Pkt4::pack() { if (!hwaddr_) { isc_throw(InvalidOperation, "Can't build Pkt4 packet. HWAddr not set."); } size_t hw_len = hwaddr_->hwaddr_.size(); bufferOut_.writeUint8(op_); bufferOut_.writeUint8(hwaddr_->htype_); bufferOut_.writeUint8(hw_len < MAX_CHADDR_LEN ? hw_len : MAX_CHADDR_LEN); bufferOut_.writeUint8(hops_); bufferOut_.writeUint32(transid_); bufferOut_.writeUint16(secs_); bufferOut_.writeUint16(flags_); bufferOut_.writeUint32(ciaddr_); bufferOut_.writeUint32(yiaddr_); bufferOut_.writeUint32(siaddr_); bufferOut_.writeUint32(giaddr_); if (hw_len <= MAX_CHADDR_LEN) { // 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 < MAX_CHADDR_LEN ? hw_len : MAX_CHADDR_LEN) ); hw_len = MAX_CHADDR_LEN - hw_len; } else { hw_len = MAX_CHADDR_LEN; } // write (len) bytes of padding vector zeros(hw_len, 0); bufferOut_.writeData(&zeros[0], hw_len); // bufferOut_.writeData(chaddr_, MAX_CHADDR_LEN); bufferOut_.writeData(sname_, MAX_SNAME_LEN); bufferOut_.writeData(file_, MAX_FILE_LEN); // write DHCP magic cookie bufferOut_.writeUint32(DHCP_OPTIONS_COOKIE); LibDHCP::packOptions(bufferOut_, options_); // add END option that indicates end of options // (End option is very simple, just a 255 octet) bufferOut_.writeUint8(DHO_END); return (true); } void Pkt4::unpack() { // input buffer (used during message reception) isc::util::InputBuffer bufferIn(&data_[0], data_.size()); if (bufferIn.getLength() < DHCPV4_PKT_HDR_LEN) { isc_throw(OutOfRange, "Received truncated DHCPv4 packet (len=" << bufferIn.getLength() << " received, at least " << DHCPV4_PKT_HDR_LEN << "is expected"); } op_ = bufferIn.readUint8(); uint8_t htype = bufferIn.readUint8(); uint8_t 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()); vector hw_addr(MAX_CHADDR_LEN, 0); bufferIn.readVector(hw_addr, MAX_CHADDR_LEN); bufferIn.readData(sname_, MAX_SNAME_LEN); bufferIn.readData(file_, MAX_FILE_LEN); hw_addr.resize(hlen); hwaddr_ = HWAddrPtr(new HWAddr(hw_addr, htype)); 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. isc_throw(InvalidOperation, "Received BOOTP packet. BOOTP is not supported."); } 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"); } size_t opts_len = bufferIn.getLength() - bufferIn.getPosition(); vector optsBuffer; // First use of readVector. bufferIn.readVector(optsBuffer, opts_len); LibDHCP::unpackOptions4(optsBuffer, options_); // @todo check will need to be called separately, so hooks can be called // after the packet is parsed, but before its content is verified check(); } void Pkt4::check() { uint8_t msg_type = getType(); if (msg_type > DHCPLEASEACTIVE) { isc_throw(BadValue, "Invalid DHCP message type received: " << static_cast(msg_type)); } } uint8_t Pkt4::getType() const { OptionPtr generic = getOption(DHO_DHCP_MESSAGE_TYPE); if (!generic) { isc_throw(Unexpected, "Missing DHCP Message Type option"); } // Check if Message Type is specified as OptionInt boost::shared_ptr > type_opt = boost::dynamic_pointer_cast >(generic); if (type_opt) { return (type_opt->getValue()); } // Try to use it as generic option return (generic->getUint8()); } void Pkt4::setType(uint8_t dhcp_type) { OptionPtr opt = getOption(DHO_DHCP_MESSAGE_TYPE); if (opt) { // There is message type option already, update it opt->setUint8(dhcp_type); } else { // There is no message type option yet, add it std::vector tmp(1, dhcp_type); opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp)); addOption(opt); } } void Pkt4::repack() { bufferOut_.writeData(&data_[0], data_.size()); } std::string Pkt4::toText() { stringstream tmp; tmp << "localAddr=" << local_addr_.toText() << ":" << local_port_ << " remoteAddr=" << remote_addr_.toText() << ":" << remote_port_ << ", msgtype=" << static_cast(getType()) << ", 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; } return tmp.str(); } void Pkt4::setHWAddr(uint8_t hType, uint8_t hlen, const std::vector& mac_addr) { /// @todo Rewrite this once support for client-identifier option /// is implemented (ticket 1228?) if (hlen > MAX_CHADDR_LEN) { isc_throw(OutOfRange, "Hardware address (len=" << hlen << " too long. Max " << MAX_CHADDR_LEN << " supported."); } else if (mac_addr.empty() && (hlen > 0) ) { isc_throw(OutOfRange, "Invalid HW Address specified"); } hwaddr_.reset(new HWAddr(mac_addr, hType)); } void Pkt4::setHWAddr(const HWAddrPtr& addr) { if (!addr) { isc_throw(BadValue, "Setting hw address to NULL is forbidden"); } hwaddr_ = addr; } 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."); } else if (sname == NULL) { isc_throw(InvalidParameter, "Invalid sname specified"); } std::copy(&sname[0], &sname[snameLen], &sname_[0]); std::fill(&sname_[snameLen], &sname_[MAX_SNAME_LEN], 0); // 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."); } else if (file == NULL) { isc_throw(InvalidParameter, "Invalid file name specified"); } std::copy(&file[0], &file[fileLen], &file_[0]); std::fill(&file_[fileLen], &file_[MAX_FILE_LEN], 0); // No need to store fileLen as any empty space is filled with 0s } 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(dhcpType) ); } } 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 <= MAX_CHADDR_LEN ? len : MAX_CHADDR_LEN); } void Pkt4::addOption(boost::shared_ptr