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