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 "dhcpserver.h" 18 19#include "dhcp.h" 20#include "log.h" 21#include "message.h" 22 23#include <arpa/inet.h> 24#include <errno.h> 25#include <linux/sockios.h> 26#include <net/if.h> 27#include <netinet/in.h> 28#include <poll.h> 29#include <string.h> 30#include <sys/types.h> 31#include <sys/socket.h> 32#include <unistd.h> 33 34#include <cutils/properties.h> 35 36static const int kMaxDnsServers = 4; 37 38DhcpServer::DhcpServer(in_addr_t dhcpRangeStart, 39 in_addr_t dhcpRangeEnd, 40 in_addr_t netmask, 41 in_addr_t gateway, 42 unsigned int excludeInterface) : 43 mNextAddressOffset(0), 44 mDhcpRangeStart(dhcpRangeStart), 45 mDhcpRangeEnd(dhcpRangeEnd), 46 mNetmask(netmask), 47 mGateway(gateway), 48 mExcludeInterface(excludeInterface) 49{ 50} 51 52Result DhcpServer::init() { 53 Result res = mSocket.open(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 54 if (!res) { 55 return res; 56 } 57 res = mSocket.enableOption(SOL_IP, IP_PKTINFO); 58 if (!res) { 59 return res; 60 } 61 res = mSocket.enableOption(SOL_SOCKET, SO_BROADCAST); 62 if (!res) { 63 return res; 64 } 65 66 res = mSocket.bindIp(INADDR_ANY, PORT_BOOTP_SERVER); 67 if (!res) { 68 return res; 69 } 70 71 return Result::success(); 72} 73 74Result DhcpServer::run() { 75 // Block all signals while we're running. This way we don't have to deal 76 // with things like EINTR. We then uses ppoll to set the original mask while 77 // polling. This way polling can be interrupted but socket writing, reading 78 // and ioctl remain interrupt free. If a signal arrives while we're blocking 79 // it it will be placed in the signal queue and handled once ppoll sets the 80 // original mask. This way no signals are lost. 81 sigset_t blockMask, originalMask; 82 int status = ::sigfillset(&blockMask); 83 if (status != 0) { 84 return Result::error("Unable to fill signal set: %s", strerror(errno)); 85 } 86 status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask); 87 if (status != 0) { 88 return Result::error("Unable to set signal mask: %s", strerror(errno)); 89 } 90 91 struct pollfd fds; 92 fds.fd = mSocket.get(); 93 fds.events = POLLIN; 94 Message message; 95 while ((status = ::ppoll(&fds, 1, nullptr, &originalMask)) >= 0) { 96 if (status == 0) { 97 // Timeout 98 continue; 99 } 100 101 unsigned int interfaceIndex = 0; 102 Result res = mSocket.receiveFromInterface(&message, 103 &interfaceIndex); 104 if (!res) { 105 ALOGE("Failed to recieve on socket: %s", res.c_str()); 106 continue; 107 } 108 if (interfaceIndex == 0 || mExcludeInterface == interfaceIndex) { 109 // Received packet on unknown or unwanted interface, drop it 110 continue; 111 } 112 if (!message.isValidDhcpMessage(OP_BOOTREQUEST)) { 113 // Not a DHCP request, drop it 114 continue; 115 } 116 switch (message.type()) { 117 case DHCPDISCOVER: 118 // Someone is trying to find us, let them know we exist 119 sendDhcpOffer(message, interfaceIndex); 120 break; 121 case DHCPREQUEST: 122 // Someone wants a lease based on an offer 123 if (isValidDhcpRequest(message, interfaceIndex)) { 124 // The request matches our offer, acknowledge it 125 sendAck(message, interfaceIndex); 126 } else { 127 // Request for something other than we offered, denied 128 sendNack(message, interfaceIndex); 129 } 130 break; 131 } 132 } 133 // Polling failed, exit 134 return Result::error("Polling failed: %s", strerror(errno)); 135} 136 137Result DhcpServer::sendMessage(unsigned int interfaceIndex, 138 in_addr_t /*sourceAddress*/, 139 const Message& message) { 140 return mSocket.sendOnInterface(interfaceIndex, 141 INADDR_BROADCAST, 142 PORT_BOOTP_CLIENT, 143 message); 144} 145 146void DhcpServer::sendDhcpOffer(const Message& message, 147 unsigned int interfaceIndex ) { 148 updateDnsServers(); 149 in_addr_t offerAddress; 150 Result res = getOfferAddress(interfaceIndex, 151 message.dhcpData.chaddr, 152 &offerAddress); 153 if (!res) { 154 ALOGE("Failed to get address for offer: %s", res.c_str()); 155 return; 156 } 157 in_addr_t serverAddress; 158 res = getInterfaceAddress(interfaceIndex, &serverAddress); 159 if (!res) { 160 ALOGE("Failed to get address for interface %u: %s", 161 interfaceIndex, res.c_str()); 162 return; 163 } 164 165 Message offer = Message::offer(message, 166 serverAddress, 167 offerAddress, 168 mNetmask, 169 mGateway, 170 mDnsServers.data(), 171 mDnsServers.size()); 172 res = sendMessage(interfaceIndex, serverAddress, offer); 173 if (!res) { 174 ALOGE("Failed to send DHCP offer: %s", res.c_str()); 175 } 176} 177 178void DhcpServer::sendAck(const Message& message, unsigned int interfaceIndex) { 179 updateDnsServers(); 180 in_addr_t offerAddress, serverAddress; 181 Result res = getOfferAddress(interfaceIndex, 182 message.dhcpData.chaddr, 183 &offerAddress); 184 if (!res) { 185 ALOGE("Failed to get address for offer: %s", res.c_str()); 186 return; 187 } 188 res = getInterfaceAddress(interfaceIndex, &serverAddress); 189 if (!res) { 190 ALOGE("Failed to get address for interface %u: %s", 191 interfaceIndex, res.c_str()); 192 return; 193 } 194 Message ack = Message::ack(message, 195 serverAddress, 196 offerAddress, 197 mNetmask, 198 mGateway, 199 mDnsServers.data(), 200 mDnsServers.size()); 201 res = sendMessage(interfaceIndex, serverAddress, ack); 202 if (!res) { 203 ALOGE("Failed to send DHCP ack: %s", res.c_str()); 204 } 205} 206 207void DhcpServer::sendNack(const Message& message, unsigned int interfaceIndex) { 208 in_addr_t serverAddress; 209 Result res = getInterfaceAddress(interfaceIndex, &serverAddress); 210 if (!res) { 211 ALOGE("Failed to get address for interface %u: %s", 212 interfaceIndex, res.c_str()); 213 return; 214 } 215 Message nack = Message::nack(message, serverAddress); 216 res = sendMessage(interfaceIndex, serverAddress, nack); 217 if (!res) { 218 ALOGE("Failed to send DHCP nack: %s", res.c_str()); 219 } 220} 221 222bool DhcpServer::isValidDhcpRequest(const Message& message, 223 unsigned int interfaceIndex) { 224 in_addr_t offerAddress; 225 Result res = getOfferAddress(interfaceIndex, 226 message.dhcpData.chaddr, 227 &offerAddress); 228 if (!res) { 229 ALOGE("Failed to get address for offer: %s", res.c_str()); 230 return false; 231 } 232 if (message.requestedIp() != offerAddress) { 233 ALOGE("Client requested a different IP address from the offered one"); 234 return false; 235 } 236 return true; 237} 238 239void DhcpServer::updateDnsServers() { 240 char key[64]; 241 char value[PROPERTY_VALUE_MAX]; 242 mDnsServers.clear(); 243 for (int i = 1; i <= kMaxDnsServers; ++i) { 244 snprintf(key, sizeof(key), "net.eth0.dns%d", i); 245 if (property_get(key, value, nullptr) > 0) { 246 struct in_addr address; 247 if (::inet_pton(AF_INET, value, &address) > 0) { 248 mDnsServers.push_back(address.s_addr); 249 } 250 } 251 } 252} 253 254Result DhcpServer::getInterfaceAddress(unsigned int interfaceIndex, 255 in_addr_t* address) { 256 char interfaceName[IF_NAMESIZE + 1]; 257 if (if_indextoname(interfaceIndex, interfaceName) == nullptr) { 258 return Result::error("Failed to get interface name for index %u: %s", 259 interfaceIndex, strerror(errno)); 260 } 261 struct ifreq request; 262 memset(&request, 0, sizeof(request)); 263 request.ifr_addr.sa_family = AF_INET; 264 strncpy(request.ifr_name, interfaceName, IFNAMSIZ - 1); 265 266 if (::ioctl(mSocket.get(), SIOCGIFADDR, &request) == -1) { 267 return Result::error("Failed to get address for interface %s: %s", 268 interfaceName, strerror(errno)); 269 } 270 271 auto inAddr = reinterpret_cast<struct sockaddr_in*>(&request.ifr_addr); 272 *address = inAddr->sin_addr.s_addr; 273 274 return Result::success(); 275} 276 277Result DhcpServer::getOfferAddress(unsigned int interfaceIndex, 278 const uint8_t* macAddress, 279 in_addr_t* address) { 280 Lease key(interfaceIndex, macAddress); 281 282 // Find or create entry, if it's created it will be zero and we update it 283 in_addr_t& value = mLeases[key]; 284 if (value == 0) { 285 // Addresses are stored in network byte order so when doing math on them 286 // they have to be converted to host byte order 287 in_addr_t nextAddress = ntohl(mDhcpRangeStart) + mNextAddressOffset; 288 uint8_t lastAddressByte = nextAddress & 0xFF; 289 while (lastAddressByte == 0xFF || lastAddressByte == 0) { 290 // The address ends in .255 or .0 which means it's a broadcast or 291 // network address respectively. Increase it further to avoid this. 292 ++nextAddress; 293 ++mNextAddressOffset; 294 } 295 if (nextAddress <= ntohl(mDhcpRangeEnd)) { 296 // And then converted back again 297 value = htonl(nextAddress); 298 ++mNextAddressOffset; 299 } else { 300 // Ran out of addresses 301 return Result::error("DHCP server is out of addresses"); 302 } 303 } 304 *address = value; 305 return Result::success(); 306} 307 308