• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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