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