1 // 2 // Copyright (C) 2015 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "dhcp_client/dhcp_message.h" 18 19 #include <net/if.h> 20 #include <net/if_arp.h> 21 #include <netinet/in.h> 22 23 #include <memory> 24 #include <set> 25 #include <string> 26 #include <utility> 27 #include <vector> 28 29 #include <base/logging.h> 30 31 #include "dhcp_client/dhcp_options.h" 32 #include "dhcp_client/dhcp_options_writer.h" 33 34 using shill::ByteString; 35 36 namespace dhcp_client { 37 38 namespace { 39 const int kClientHardwareAddressLength = 16; 40 const int kServerNameLength = 64; 41 const int kBootFileLength = 128; 42 const uint32_t kMagicCookie = 0x63825363; 43 const size_t kDHCPMessageMaxLength = 548; 44 const size_t kDHCPMessageMinLength = 236; 45 const uint8_t kDHCPMessageBootRequest = 1; 46 const uint8_t kDHCPMessageBootReply = 2; 47 48 // Follow the naming in rfc2131 for this struct. 49 struct __attribute__((__packed__)) RawDHCPMessage { 50 uint8_t op; 51 uint8_t htype; 52 uint8_t hlen; 53 uint8_t hops; 54 uint32_t xid; 55 uint16_t secs; 56 uint16_t flags; 57 uint32_t ciaddr; 58 uint32_t yiaddr; 59 uint32_t siaddr; 60 uint32_t giaddr; 61 uint8_t chaddr[kClientHardwareAddressLength]; 62 uint8_t sname[kServerNameLength]; 63 uint8_t file[kBootFileLength]; 64 uint32_t cookie; 65 uint8_t options[kDHCPOptionLength]; 66 }; 67 } // namespace 68 69 DHCPMessage::DHCPMessage() 70 : requested_ip_address_(0), 71 lease_time_(0), 72 message_type_(0), 73 server_identifier_(0), 74 renewal_time_(0), 75 rebinding_time_(0) { 76 options_map_.insert(std::make_pair(kDHCPOptionMessageType, 77 ParserContext(new UInt8Parser(), &message_type_))); 78 options_map_.insert(std::make_pair(kDHCPOptionLeaseTime, 79 ParserContext(new UInt32Parser(), &lease_time_))); 80 options_map_.insert(std::make_pair(kDHCPOptionMessage, 81 ParserContext(new StringParser(), &error_message_))); 82 options_map_.insert(std::make_pair(kDHCPOptionSubnetMask, 83 ParserContext(new UInt32Parser(), &subnet_mask_))); 84 options_map_.insert(std::make_pair(kDHCPOptionServerIdentifier, 85 ParserContext(new UInt32Parser(), &server_identifier_))); 86 options_map_.insert(std::make_pair(kDHCPOptionRenewalTime, 87 ParserContext(new UInt32Parser(), &renewal_time_))); 88 options_map_.insert(std::make_pair(kDHCPOptionRebindingTime, 89 ParserContext(new UInt32Parser(), &rebinding_time_))); 90 options_map_.insert(std::make_pair(kDHCPOptionDNSServer, 91 ParserContext(new UInt32ListParser(), &dns_server_))); 92 options_map_.insert(std::make_pair(kDHCPOptionRouter, 93 ParserContext(new UInt32ListParser(), &router_))); 94 options_map_.insert(std::make_pair(kDHCPOptionDomainName, 95 ParserContext(new StringParser(), &domain_name_))); 96 options_map_.insert(std::make_pair(kDHCPOptionVendorSpecificInformation, 97 ParserContext(new ByteArrayParser(), &vendor_specific_info_))); 98 } 99 100 DHCPMessage::~DHCPMessage() {} 101 102 bool DHCPMessage::InitFromBuffer(const unsigned char* buffer, 103 size_t length, 104 DHCPMessage* message) { 105 if (buffer == NULL) { 106 LOG(ERROR) << "Invalid buffer address"; 107 return false; 108 } 109 if (length < kDHCPMessageMinLength || length > kDHCPMessageMaxLength) { 110 LOG(ERROR) << "Invalid DHCP message length"; 111 return false; 112 } 113 const RawDHCPMessage* raw_message 114 = reinterpret_cast<const RawDHCPMessage*>(buffer); 115 size_t options_length = reinterpret_cast<const unsigned char*>(raw_message) + 116 length - reinterpret_cast<const unsigned char*>(raw_message->options) + 1; 117 message->opcode_ = raw_message->op; 118 message->hardware_address_type_ = raw_message->htype; 119 message->hardware_address_length_ = raw_message->hlen; 120 if (message->hardware_address_length_ > kClientHardwareAddressLength) { 121 LOG(ERROR) << "Invalid hardware address length"; 122 return false; 123 } 124 message->relay_hops_ = raw_message->hops; 125 message->transaction_id_ = ntohl(raw_message->xid); 126 message->seconds_ = ntohs(raw_message->secs); 127 message->flags_ = ntohs(raw_message->flags); 128 message->client_ip_address_ = ntohl(raw_message->ciaddr); 129 message->your_ip_address_ = ntohl(raw_message->yiaddr); 130 message->next_server_ip_address_ = ntohl(raw_message->siaddr); 131 message->agent_ip_address_ = ntohl(raw_message->giaddr); 132 message->cookie_ = ntohl(raw_message->cookie); 133 message->client_hardware_address_ = ByteString( 134 reinterpret_cast<const char*>(raw_message->chaddr), 135 message->hardware_address_length_); 136 message->servername_.assign(reinterpret_cast<const char*>(raw_message->sname), 137 kServerNameLength); 138 message->bootfile_.assign(reinterpret_cast<const char*>(raw_message->file), 139 kBootFileLength); 140 // Validate the DHCP Message 141 if (!message->IsValid()) { 142 return false; 143 } 144 if (!message->ParseDHCPOptions(raw_message->options, options_length)) { 145 LOG(ERROR) << "Failed to parse DHCP options"; 146 return false; 147 } 148 return true; 149 } 150 151 bool DHCPMessage::ParseDHCPOptions(const uint8_t* options, 152 size_t options_length) { 153 // DHCP options are in TLV format. 154 // T: tag, L: length, V: value(data) 155 // RFC 1497, RFC 1533, RFC 2132 156 const uint8_t* ptr = options; 157 const uint8_t* end_ptr = options + options_length; 158 std::set<uint8_t> options_set; 159 while (ptr < end_ptr) { 160 uint8_t option_code = *ptr++; 161 int option_code_int = static_cast<int>(option_code); 162 if (option_code == kDHCPOptionPad) { 163 continue; 164 } else if (option_code == kDHCPOptionEnd) { 165 // We reach the end of the option field. 166 // Validate the options before we return. 167 return ContainsValidOptions(options_set); 168 } 169 if (ptr >= end_ptr) { 170 LOG(ERROR) << "Failed to decode dhcp options, no option length field" 171 " for option: " << option_code_int; 172 return false; 173 } 174 uint8_t option_length = *ptr++; 175 if (ptr + option_length >= end_ptr) { 176 LOG(ERROR) << "Failed to decode dhcp options, invalid option length field" 177 " for option: " << option_code_int; 178 return false; 179 } 180 if (options_set.find(option_code) != options_set.end()) { 181 LOG(ERROR) << "Found repeated DHCP option: " << option_code_int; 182 return false; 183 } 184 // Here we find a valid DHCP option. 185 auto it = options_map_.find(option_code); 186 if (it != options_map_.end()) { 187 ParserContext* context = &(it->second); 188 if (!context->parser->GetOption(ptr, option_length, context->output)) { 189 return false; 190 } 191 options_set.insert(option_code); 192 } else { 193 DLOG(INFO) << "Ignore DHCP option: " << option_code_int; 194 } 195 // Move to next tag. 196 ptr += option_length; 197 } 198 // Reach the end of message without seeing kDHCPOptionEnd. 199 LOG(ERROR) << "Broken DHCP options without END tag."; 200 return false; 201 } 202 203 bool DHCPMessage::ContainsValidOptions(const std::set<uint8_t>& options_set) { 204 // A DHCP message must contain option 53: DHCP Message Type. 205 if (options_set.find(kDHCPOptionMessageType) == options_set.end()) { 206 LOG(ERROR) << "Faied to find option 53: DHCP Message Type."; 207 return false; 208 } 209 if (message_type_ != kDHCPMessageTypeOffer && 210 message_type_ != kDHCPMessageTypeAck && 211 message_type_ != kDHCPMessageTypeNak) { 212 LOG(ERROR) << "Invalid DHCP Message Type: " 213 << static_cast<int>(message_type_); 214 return false; 215 } 216 // A DHCP Offer message must contain option 51: IP Address Lease Time. 217 if (message_type_ == kDHCPMessageTypeOffer) { 218 if (options_set.find(kDHCPOptionLeaseTime) == options_set.end()) { 219 LOG(ERROR) << "Faied to find option 51: IP Address Lease Time"; 220 return false; 221 } 222 } 223 // A message from DHCP server must contain option 54: Server Identifier. 224 if (options_set.find(kDHCPOptionServerIdentifier) == options_set.end()) { 225 LOG(ERROR) << "Faied to find option 54: Server Identifier."; 226 return false; 227 } 228 return true; 229 } 230 231 bool DHCPMessage::IsValid() { 232 if (opcode_ != kDHCPMessageBootReply) { 233 LOG(ERROR) << "Invalid DHCP message op code"; 234 return false; 235 } 236 if (hardware_address_type_ != ARPHRD_ETHER) { 237 LOG(ERROR) << "DHCP message device family id does not match"; 238 return false; 239 } 240 if (hardware_address_length_ != IFHWADDRLEN) { 241 LOG(ERROR) << 242 "DHCP message device hardware address length does not match"; 243 return false; 244 } 245 // We have nothing to do with the 'hops' field. 246 247 // The reply message from server should have the same xid we cached in client. 248 // DHCP state machine will take charge of this checking. 249 250 // According to RFC 2131, all secs field in reply messages should be 0. 251 if (seconds_) { 252 LOG(ERROR) << "Invalid DHCP message secs"; 253 return false; 254 } 255 256 // Check broadcast flags. 257 // It should be 0 because we do not request broadcast reply. 258 if (flags_) { 259 LOG(ERROR) << "Invalid DHCP message flags"; 260 return false; 261 } 262 263 // We need to ensure the message contains the correct client hardware address. 264 // DHCP state machine will take charge of this checking. 265 266 // We do not use the bootfile field. 267 if (cookie_ != kMagicCookie) { 268 LOG(ERROR) << "DHCP message cookie does not match"; 269 return false; 270 } 271 return true; 272 } 273 274 bool DHCPMessage::Serialize(ByteString* data) const { 275 RawDHCPMessage raw_message; 276 raw_message.op = opcode_; 277 raw_message.htype = hardware_address_type_; 278 raw_message.hlen = hardware_address_length_; 279 raw_message.hops = relay_hops_; 280 raw_message.xid = htonl(transaction_id_); 281 raw_message.secs = htons(seconds_); 282 raw_message.flags = htons(flags_); 283 raw_message.ciaddr = htonl(client_ip_address_); 284 raw_message.yiaddr = htonl(your_ip_address_); 285 raw_message.siaddr = htonl(next_server_ip_address_); 286 raw_message.giaddr = htonl(agent_ip_address_); 287 raw_message.cookie = htonl(cookie_); 288 memcpy(raw_message.chaddr, 289 client_hardware_address_.GetConstData(), 290 hardware_address_length_); 291 if (servername_.length() >= kServerNameLength) { 292 LOG(ERROR) << "Invalid server name length: " << servername_.length(); 293 return false; 294 } 295 memcpy(raw_message.sname, 296 servername_.c_str(), 297 servername_.length()); 298 raw_message.sname[servername_.length()] = 0; 299 if (bootfile_.length() >= kBootFileLength) { 300 LOG(ERROR) << "Invalid boot file length: " << bootfile_.length(); 301 return false; 302 } 303 memcpy(raw_message.file, 304 bootfile_.c_str(), 305 bootfile_.length()); 306 raw_message.file[bootfile_.length()] = 0; 307 data->Append(ByteString(reinterpret_cast<const char*>(&raw_message), 308 sizeof(raw_message) - kDHCPOptionLength)); 309 // Append DHCP options to the message. 310 DHCPOptionsWriter* options_writer = DHCPOptionsWriter::GetInstance(); 311 if (options_writer->WriteUInt8Option(data, 312 kDHCPOptionMessageType, 313 message_type_) == -1) { 314 LOG(ERROR) << "Failed to write message type option"; 315 return false; 316 } 317 if (requested_ip_address_ != 0) { 318 if (options_writer->WriteUInt32Option(data, 319 kDHCPOptionRequestedIPAddr, 320 requested_ip_address_) == -1) { 321 LOG(ERROR) << "Failed to write requested ip address option"; 322 return false; 323 } 324 } 325 if (lease_time_ != 0) { 326 if (options_writer->WriteUInt32Option(data, 327 kDHCPOptionLeaseTime, 328 lease_time_) == -1) { 329 LOG(ERROR) << "Failed to write lease time option"; 330 return false; 331 } 332 } 333 if (server_identifier_ != 0) { 334 if (options_writer->WriteUInt32Option(data, 335 kDHCPOptionServerIdentifier, 336 server_identifier_) == -1) { 337 LOG(ERROR) << "Failed to write server identifier option"; 338 return false; 339 } 340 } 341 if (error_message_.size() != 0) { 342 if (options_writer->WriteStringOption(data, 343 kDHCPOptionMessage, 344 error_message_) == -1) { 345 LOG(ERROR) << "Failed to write error message option"; 346 return false; 347 } 348 } 349 if (parameter_request_list_.size() != 0) { 350 if (options_writer->WriteUInt8ListOption(data, 351 kDHCPOptionParameterRequestList, 352 parameter_request_list_) == -1) { 353 LOG(ERROR) << "Failed to write parameter request list"; 354 return false; 355 } 356 } 357 // TODO(nywang): Append other options. 358 // Append end tag. 359 if (options_writer->WriteEndTag(data) == -1) { 360 LOG(ERROR) << "Failed to write DHCP options end tag"; 361 return false; 362 } 363 // Ensure we do not exceed the maximum length. 364 if (data->GetLength() > kDHCPMessageMaxLength) { 365 LOG(ERROR) << "DHCP message length exceeds the limit"; 366 return false; 367 } 368 return true; 369 } 370 371 uint16_t DHCPMessage::ComputeChecksum(const uint8_t* data, size_t len) { 372 uint32_t sum = 0; 373 374 while (len > 1) { 375 sum += static_cast<uint32_t>(data[0]) << 8 | static_cast<uint32_t>(data[1]); 376 data += 2; 377 len -= 2; 378 } 379 if (len == 1) { 380 sum += static_cast<uint32_t>(*data) << 8; 381 } 382 sum = (sum >> 16) + (sum & 0xffff); 383 sum += (sum >> 16); 384 385 return ~static_cast<uint16_t>(sum); 386 } 387 388 void DHCPMessage::SetClientIdentifier( 389 const ByteString& client_identifier) { 390 client_identifier_ = client_identifier; 391 } 392 393 void DHCPMessage::SetClientIPAddress(uint32_t client_ip_address) { 394 client_ip_address_ = client_ip_address; 395 } 396 397 void DHCPMessage::SetClientHardwareAddress( 398 const ByteString& client_hardware_address) { 399 client_hardware_address_ = client_hardware_address; 400 } 401 402 void DHCPMessage::SetErrorMessage(const std::string& error_message) { 403 error_message_ = error_message; 404 } 405 406 void DHCPMessage::SetLeaseTime(uint32_t lease_time) { 407 lease_time_ = lease_time; 408 } 409 410 void DHCPMessage::SetMessageType(uint8_t message_type) { 411 message_type_ = message_type; 412 } 413 414 void DHCPMessage::SetParameterRequestList( 415 const std::vector<uint8_t>& parameter_request_list) { 416 parameter_request_list_ = parameter_request_list; 417 } 418 419 void DHCPMessage::SetRequestedIpAddress(uint32_t requested_ip_address) { 420 requested_ip_address_ = requested_ip_address; 421 } 422 423 void DHCPMessage::SetServerIdentifier(uint32_t server_identifier) { 424 server_identifier_ = server_identifier; 425 } 426 427 void DHCPMessage::SetTransactionID(uint32_t transaction_id) { 428 transaction_id_ = transaction_id; 429 } 430 431 void DHCPMessage::SetVendorSpecificInfo( 432 const shill::ByteString& vendor_specific_info) { 433 vendor_specific_info_ = vendor_specific_info; 434 } 435 436 void DHCPMessage::InitRequest(DHCPMessage* message) { 437 message->opcode_ = kDHCPMessageBootRequest; 438 message->hardware_address_type_ = ARPHRD_ETHER; 439 message->hardware_address_length_ = IFHWADDRLEN; 440 message->relay_hops_ = 0; 441 // Seconds since DHCP process started. 442 // 0 is also valid according to RFC 2131. 443 message->seconds_ = 0; 444 // Only firewire (IEEE 1394) and InfiniBand interfaces 445 // require broadcast flag. 446 message->flags_ = 0; 447 // Should be zero in client's messages. 448 message->your_ip_address_ = 0; 449 // Should be zero in client's messages. 450 message->next_server_ip_address_ = 0; 451 // Should be zero in client's messages. 452 message->agent_ip_address_ = 0; 453 message->cookie_ = kMagicCookie; 454 } 455 456 } // namespace dhcp_client 457