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