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
Message()41 Message::Message() {
42 memset(&dhcpData, 0, sizeof(dhcpData));
43 mSize = 0;
44 }
45
Message(const uint8_t * data,size_t size)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
discover(const uint8_t (& sourceMac)[ETH_ALEN])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
request(const uint8_t (& sourceMac)[ETH_ALEN],in_addr_t requestAddress,in_addr_t serverAddress)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
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)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
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)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
nack(const Message & sourceMessage,in_addr_t serverAddress)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
isValidDhcpMessage(uint8_t expectedOp,uint32_t expectedXid) const157 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
isValidDhcpMessage(uint8_t expectedOp) const169 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
optionsSize() const202 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
type() const211 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
serverId() const220 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
requestedIp() const229 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
Message(uint8_t operation,const uint8_t (& macAddress)[ETH_ALEN],uint8_t type)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
addOption(uint8_t type,const void * data,uint8_t size)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
endOptions()279 void Message::endOptions() {
280 uint8_t* opts = nextOption();
281
282 *opts++ = OPT_END;
283
284 updateSize(opts);
285 }
286
getOption(uint8_t expectedOptCode,uint8_t * length) const287 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
nextOption()307 uint8_t* Message::nextOption() {
308 return reinterpret_cast<uint8_t*>(&dhcpData) + size();
309 }
310
updateSize(uint8_t * optionsEnd)311 void Message::updateSize(uint8_t* optionsEnd) {
312 mSize = optionsEnd - reinterpret_cast<uint8_t*>(&dhcpData);
313 }
314
315