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