1 /* 2 * Copyright 2017, 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 "message.h" 18 #include "dhcp.h" 19 20 #include <string.h> 21 22 #include <vector> 23 24 static uint32_t sNextTransactionId = 1; 25 26 static const ptrdiff_t kOptionOffset = 7; 27 28 // The default lease time in seconds 29 static const uint32_t kDefaultLeaseTime = 10 * 60; 30 31 // The parameters that the client would like to receive from the server 32 static const uint8_t kRequestParameters[] = { OPT_SUBNET_MASK, 33 OPT_GATEWAY, 34 OPT_DNS, 35 OPT_BROADCAST_ADDR, 36 OPT_LEASE_TIME, 37 OPT_T1, 38 OPT_T2, 39 OPT_MTU }; 40 41 Message::Message() { 42 memset(&dhcpData, 0, sizeof(dhcpData)); 43 mSize = 0; 44 } 45 46 Message::Message(const uint8_t* data, size_t size) { 47 if (size <= sizeof(dhcpData)) { 48 memcpy(&dhcpData, data, size); 49 mSize = size; 50 } else { 51 memset(&dhcpData, 0, sizeof(dhcpData)); 52 mSize = 0; 53 } 54 } 55 56 Message Message::discover(const uint8_t (&sourceMac)[ETH_ALEN]) { 57 Message message(OP_BOOTREQUEST, 58 sourceMac, 59 static_cast<uint8_t>(DHCPDISCOVER)); 60 61 message.addOption(OPT_PARAMETER_LIST, kRequestParameters); 62 message.endOptions(); 63 64 return message; 65 } 66 67 Message Message::request(const uint8_t (&sourceMac)[ETH_ALEN], 68 in_addr_t requestAddress, 69 in_addr_t serverAddress) { 70 71 Message message(OP_BOOTREQUEST, 72 sourceMac, 73 static_cast<uint8_t>(DHCPREQUEST)); 74 75 message.addOption(OPT_PARAMETER_LIST, kRequestParameters); 76 message.addOption(OPT_REQUESTED_IP, requestAddress); 77 message.addOption(OPT_SERVER_ID, serverAddress); 78 message.endOptions(); 79 80 return message; 81 } 82 83 Message Message::offer(const Message& sourceMessage, 84 in_addr_t serverAddress, 85 in_addr_t offeredAddress, 86 in_addr_t offeredNetmask, 87 in_addr_t offeredGateway, 88 const in_addr_t* offeredDnsServers, 89 size_t numOfferedDnsServers) { 90 91 uint8_t macAddress[ETH_ALEN]; 92 memcpy(macAddress, sourceMessage.dhcpData.chaddr, sizeof(macAddress)); 93 Message message(OP_BOOTREPLY, macAddress, static_cast<uint8_t>(DHCPOFFER)); 94 95 message.dhcpData.xid = sourceMessage.dhcpData.xid; 96 message.dhcpData.flags = sourceMessage.dhcpData.flags; 97 message.dhcpData.yiaddr = offeredAddress; 98 message.dhcpData.giaddr = sourceMessage.dhcpData.giaddr; 99 100 message.addOption(OPT_SERVER_ID, serverAddress); 101 message.addOption(OPT_LEASE_TIME, kDefaultLeaseTime); 102 message.addOption(OPT_SUBNET_MASK, offeredNetmask); 103 message.addOption(OPT_GATEWAY, offeredGateway); 104 message.addOption(OPT_DNS, 105 offeredDnsServers, 106 numOfferedDnsServers * sizeof(in_addr_t)); 107 108 message.endOptions(); 109 110 return message; 111 } 112 113 Message Message::ack(const Message& sourceMessage, 114 in_addr_t serverAddress, 115 in_addr_t offeredAddress, 116 in_addr_t offeredNetmask, 117 in_addr_t offeredGateway, 118 const in_addr_t* offeredDnsServers, 119 size_t numOfferedDnsServers) { 120 uint8_t macAddress[ETH_ALEN]; 121 memcpy(macAddress, sourceMessage.dhcpData.chaddr, sizeof(macAddress)); 122 Message message(OP_BOOTREPLY, macAddress, static_cast<uint8_t>(DHCPACK)); 123 124 message.dhcpData.xid = sourceMessage.dhcpData.xid; 125 message.dhcpData.flags = sourceMessage.dhcpData.flags; 126 message.dhcpData.yiaddr = offeredAddress; 127 message.dhcpData.giaddr = sourceMessage.dhcpData.giaddr; 128 129 message.addOption(OPT_SERVER_ID, serverAddress); 130 message.addOption(OPT_LEASE_TIME, kDefaultLeaseTime); 131 message.addOption(OPT_SUBNET_MASK, offeredNetmask); 132 message.addOption(OPT_GATEWAY, offeredGateway); 133 message.addOption(OPT_DNS, 134 offeredDnsServers, 135 numOfferedDnsServers * sizeof(in_addr_t)); 136 137 message.endOptions(); 138 139 return message; 140 } 141 142 Message Message::nack(const Message& sourceMessage, in_addr_t serverAddress) { 143 uint8_t macAddress[ETH_ALEN]; 144 memcpy(macAddress, sourceMessage.dhcpData.chaddr, sizeof(macAddress)); 145 Message message(OP_BOOTREPLY, macAddress, static_cast<uint8_t>(DHCPNAK)); 146 147 message.dhcpData.xid = sourceMessage.dhcpData.xid; 148 message.dhcpData.flags = sourceMessage.dhcpData.flags; 149 message.dhcpData.giaddr = sourceMessage.dhcpData.giaddr; 150 151 message.addOption(OPT_SERVER_ID, serverAddress); 152 message.endOptions(); 153 154 return message; 155 } 156 157 bool Message::isValidDhcpMessage(uint8_t expectedOp, 158 uint32_t expectedXid) const { 159 if (!isValidDhcpMessage(expectedOp)) { 160 return false; 161 } 162 // Only look for message with a matching transaction ID 163 if (dhcpData.xid != expectedXid) { 164 return false; 165 } 166 return true; 167 } 168 169 bool Message::isValidDhcpMessage(uint8_t expectedOp) const { 170 // Require that there is at least enough options for the DHCP cookie 171 if (dhcpData.options + 4 > end()) { 172 return false; 173 } 174 175 if (dhcpData.op != expectedOp) { 176 return false; 177 } 178 if (dhcpData.htype != HTYPE_ETHER) { 179 return false; 180 } 181 if (dhcpData.hlen != ETH_ALEN) { 182 return false; 183 } 184 185 // Need to have the correct cookie in the options 186 if (dhcpData.options[0] != OPT_COOKIE1) { 187 return false; 188 } 189 if (dhcpData.options[1] != OPT_COOKIE2) { 190 return false; 191 } 192 if (dhcpData.options[2] != OPT_COOKIE3) { 193 return false; 194 } 195 if (dhcpData.options[3] != OPT_COOKIE4) { 196 return false; 197 } 198 199 return true; 200 } 201 202 size_t Message::optionsSize() const { 203 auto options = reinterpret_cast<const uint8_t*>(&dhcpData.options); 204 const uint8_t* msgEnd = end(); 205 if (msgEnd <= options) { 206 return 0; 207 } 208 return msgEnd - options; 209 } 210 211 uint8_t Message::type() const { 212 uint8_t length = 0; 213 const uint8_t* opt = getOption(OPT_MESSAGE_TYPE, &length); 214 if (opt && length == 1) { 215 return *opt; 216 } 217 return 0; 218 } 219 220 in_addr_t Message::serverId() const { 221 uint8_t length = 0; 222 const uint8_t* opt = getOption(OPT_SERVER_ID, &length); 223 if (opt && length == 4) { 224 return *reinterpret_cast<const in_addr_t*>(opt); 225 } 226 return 0; 227 } 228 229 in_addr_t Message::requestedIp() const { 230 uint8_t length = 0; 231 const uint8_t* opt = getOption(OPT_REQUESTED_IP, &length); 232 if (opt && length == 4) { 233 return *reinterpret_cast<const in_addr_t*>(opt); 234 } 235 return 0; 236 } 237 238 Message::Message(uint8_t operation, 239 const uint8_t (&macAddress)[ETH_ALEN], 240 uint8_t type) { 241 memset(&dhcpData, 0, sizeof(dhcpData)); 242 243 dhcpData.op = operation; 244 dhcpData.htype = HTYPE_ETHER; 245 dhcpData.hlen = ETH_ALEN; 246 dhcpData.hops = 0; 247 248 dhcpData.flags = htons(FLAGS_BROADCAST); 249 250 dhcpData.xid = htonl(sNextTransactionId++); 251 252 memcpy(dhcpData.chaddr, macAddress, ETH_ALEN); 253 254 uint8_t* opts = dhcpData.options; 255 256 *opts++ = OPT_COOKIE1; 257 *opts++ = OPT_COOKIE2; 258 *opts++ = OPT_COOKIE3; 259 *opts++ = OPT_COOKIE4; 260 261 *opts++ = OPT_MESSAGE_TYPE; 262 *opts++ = 1; 263 *opts++ = type; 264 265 updateSize(opts); 266 } 267 268 void Message::addOption(uint8_t type, const void* data, uint8_t size) { 269 uint8_t* opts = nextOption(); 270 271 *opts++ = type; 272 *opts++ = size; 273 memcpy(opts, data, size); 274 opts += size; 275 276 updateSize(opts); 277 } 278 279 void Message::endOptions() { 280 uint8_t* opts = nextOption(); 281 282 *opts++ = OPT_END; 283 284 updateSize(opts); 285 } 286 287 const uint8_t* Message::getOption(uint8_t expectedOptCode, 288 uint8_t* length) const { 289 size_t optsSize = optionsSize(); 290 for (size_t i = 4; i + 2 < optsSize; ) { 291 uint8_t optCode = dhcpData.options[i]; 292 uint8_t optLen = dhcpData.options[i + 1]; 293 const uint8_t* opt = dhcpData.options + i + 2; 294 295 if (optCode == OPT_END) { 296 return nullptr; 297 } 298 if (optCode == expectedOptCode) { 299 *length = optLen; 300 return opt; 301 } 302 i += 2 + optLen; 303 } 304 return nullptr; 305 } 306 307 uint8_t* Message::nextOption() { 308 return reinterpret_cast<uint8_t*>(&dhcpData) + size(); 309 } 310 311 void Message::updateSize(uint8_t* optionsEnd) { 312 mSize = optionsEnd - reinterpret_cast<uint8_t*>(&dhcpData); 313 } 314 315