• 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 // 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