1 /*
2 * Copyright 2008, 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 #include <dirent.h>
17 #include <errno.h>
18 #include <poll.h>
19 #include <netinet/in.h>
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/select.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <net/if.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <net/if.h>
32
33 #include "../ifutils.h"
34 #include "dhcpmsg.h"
35 #include "packet.h"
36
37 #define VERBOSE 2
38
39 static int verbose = 1;
40 static char errmsg[2048];
41
42 typedef unsigned long long msecs_t;
43 #if VERBOSE
44 void dump_dhcp_msg();
45 #endif
46
get_msecs(void)47 msecs_t get_msecs(void)
48 {
49 struct timespec ts;
50
51 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
52 return 0;
53 } else {
54 return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
55 (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
56 }
57 }
58
printerr(char * fmt,...)59 void printerr(char *fmt, ...)
60 {
61 va_list ap;
62
63 va_start(ap, fmt);
64 vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
65 va_end(ap);
66
67 printf("%s\n", errmsg);
68 }
69
dhcp_lasterror()70 const char *dhcp_lasterror()
71 {
72 return errmsg;
73 }
74
fatal(const char * reason)75 int fatal(const char *reason)
76 {
77 printerr("%s: %s\n", reason, strerror(errno));
78 return -1;
79 // exit(1);
80 }
81
82 typedef struct dhcp_info dhcp_info;
83
84 struct dhcp_info {
85 uint32_t type;
86
87 uint32_t ipaddr;
88 uint32_t gateway;
89 uint32_t prefixLength;
90
91 uint32_t dns1;
92 uint32_t dns2;
93
94 uint32_t serveraddr;
95 uint32_t lease;
96 };
97
98 dhcp_info last_good_info;
99
get_dhcp_info(uint32_t * ipaddr,uint32_t * gateway,uint32_t * prefixLength,uint32_t * dns1,uint32_t * dns2,uint32_t * server,uint32_t * lease)100 void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
101 uint32_t *dns1, uint32_t *dns2, uint32_t *server,
102 uint32_t *lease)
103 {
104 *ipaddr = last_good_info.ipaddr;
105 *gateway = last_good_info.gateway;
106 *prefixLength = last_good_info.prefixLength;
107 *dns1 = last_good_info.dns1;
108 *dns2 = last_good_info.dns2;
109 *server = last_good_info.serveraddr;
110 *lease = last_good_info.lease;
111 }
112
dhcp_configure(const char * ifname,dhcp_info * info)113 static int dhcp_configure(const char *ifname, dhcp_info *info)
114 {
115 last_good_info = *info;
116 return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway,
117 info->dns1, info->dns2);
118 }
119
dhcp_type_to_name(uint32_t type)120 static const char *dhcp_type_to_name(uint32_t type)
121 {
122 switch(type) {
123 case DHCPDISCOVER: return "discover";
124 case DHCPOFFER: return "offer";
125 case DHCPREQUEST: return "request";
126 case DHCPDECLINE: return "decline";
127 case DHCPACK: return "ack";
128 case DHCPNAK: return "nak";
129 case DHCPRELEASE: return "release";
130 case DHCPINFORM: return "inform";
131 default: return "???";
132 }
133 }
134
dump_dhcp_info(dhcp_info * info)135 void dump_dhcp_info(dhcp_info *info)
136 {
137 char addr[20], gway[20];
138 printf("--- dhcp %s (%d) ---\n",
139 dhcp_type_to_name(info->type), info->type);
140 strcpy(addr, ipaddr_to_string_v4(info->ipaddr));
141 strcpy(gway, ipaddr_to_string_v4(info->gateway));
142 printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
143 if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1));
144 if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2));
145 printf("server %s, lease %d seconds\n",
146 ipaddr_to_string_v4(info->serveraddr), info->lease);
147 }
148
149
decode_dhcp_msg(dhcp_msg * msg,int len,dhcp_info * info)150 int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
151 {
152 uint8_t *x;
153 unsigned int opt;
154 int optlen;
155
156 memset(info, 0, sizeof(dhcp_info));
157 if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
158
159 len -= (DHCP_MSG_FIXED_SIZE + 4);
160
161 if (msg->options[0] != OPT_COOKIE1) return -1;
162 if (msg->options[1] != OPT_COOKIE2) return -1;
163 if (msg->options[2] != OPT_COOKIE3) return -1;
164 if (msg->options[3] != OPT_COOKIE4) return -1;
165
166 x = msg->options + 4;
167
168 while (len > 2) {
169 opt = *x++;
170 if (opt == OPT_PAD) {
171 len--;
172 continue;
173 }
174 if (opt == OPT_END) {
175 break;
176 }
177 optlen = *x++;
178 len -= 2;
179 if (optlen > len) {
180 break;
181 }
182 switch(opt) {
183 case OPT_SUBNET_MASK:
184 if (optlen >= 4) {
185 in_addr_t mask;
186 memcpy(&mask, x, 4);
187 info->prefixLength = mask_to_prefix_v4(mask);
188 }
189 break;
190 case OPT_GATEWAY:
191 if (optlen >= 4) memcpy(&info->gateway, x, 4);
192 break;
193 case OPT_DNS:
194 if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
195 if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
196 break;
197 case OPT_LEASE_TIME:
198 if (optlen >= 4) {
199 memcpy(&info->lease, x, 4);
200 info->lease = ntohl(info->lease);
201 }
202 break;
203 case OPT_SERVER_ID:
204 if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
205 break;
206 case OPT_MESSAGE_TYPE:
207 info->type = *x;
208 break;
209 default:
210 break;
211 }
212 x += optlen;
213 len -= optlen;
214 }
215
216 info->ipaddr = msg->yiaddr;
217
218 return 0;
219 }
220
221 #if VERBOSE
222
hex2str(char * buf,size_t buf_size,const unsigned char * array,int len)223 static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
224 {
225 int i;
226 char *cp = buf;
227 char *buf_end = buf + buf_size;
228 for (i = 0; i < len; i++) {
229 cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
230 }
231 }
232
dump_dhcp_msg(dhcp_msg * msg,int len)233 void dump_dhcp_msg(dhcp_msg *msg, int len)
234 {
235 unsigned char *x;
236 unsigned int n,c;
237 int optsz;
238 const char *name;
239 char buf[2048];
240
241 if (len < DHCP_MSG_FIXED_SIZE) {
242 printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
243 return;
244 }
245
246 len -= DHCP_MSG_FIXED_SIZE;
247
248 if (msg->op == OP_BOOTREQUEST)
249 name = "BOOTREQUEST";
250 else if (msg->op == OP_BOOTREPLY)
251 name = "BOOTREPLY";
252 else
253 name = "????";
254
255 c = msg->hlen > 16 ? 16 : msg->hlen;
256 hex2str(buf, sizeof(buf), msg->chaddr, c);
257
258 for (n = 0; n < 64; n++) {
259 unsigned char x = msg->sname[n];
260 if ((x < ' ') || (x > 127)) {
261 if (x == 0) break;
262 msg->sname[n] = '.';
263 }
264 }
265 msg->sname[63] = 0;
266
267 for (n = 0; n < 128; n++) {
268 unsigned char x = msg->file[n];
269 if ((x < ' ') || (x > 127)) {
270 if (x == 0) break;
271 msg->file[n] = '.';
272 }
273 }
274 msg->file[127] = 0;
275
276 if (len < 4) return;
277 len -= 4;
278 x = msg->options + 4;
279
280 while (len > 2) {
281 if (*x == 0) {
282 x++;
283 len--;
284 continue;
285 }
286 if (*x == OPT_END) {
287 break;
288 }
289 len -= 2;
290 optsz = x[1];
291 if (optsz > len) break;
292 if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
293 if ((unsigned int)optsz < sizeof(buf) - 1) {
294 n = optsz;
295 } else {
296 n = sizeof(buf) - 1;
297 }
298 memcpy(buf, &x[2], n);
299 buf[n] = '\0';
300 } else {
301 hex2str(buf, sizeof(buf), &x[2], optsz);
302 }
303 if (x[0] == OPT_MESSAGE_TYPE)
304 name = dhcp_type_to_name(x[2]);
305 else
306 name = NULL;
307 len -= optsz;
308 x = x + optsz + 2;
309 }
310 }
311
312 #endif
313
send_message(int sock,int if_index,dhcp_msg * msg,int size)314 static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
315 {
316 #if VERBOSE > 1
317 dump_dhcp_msg(msg, size);
318 #endif
319 return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
320 PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
321 }
322
is_valid_reply(dhcp_msg * msg,dhcp_msg * reply,int sz)323 static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
324 {
325 if (sz < DHCP_MSG_FIXED_SIZE) {
326 if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
327 return 0;
328 }
329 if (reply->op != OP_BOOTREPLY) {
330 if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
331 return 0;
332 }
333 if (reply->xid != msg->xid) {
334 if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
335 ntohl(msg->xid));
336 return 0;
337 }
338 if (reply->htype != msg->htype) {
339 if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype);
340 return 0;
341 }
342 if (reply->hlen != msg->hlen) {
343 if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
344 return 0;
345 }
346 if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
347 if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
348 return 0;
349 }
350 return 1;
351 }
352
353 #define STATE_SELECTING 1
354 #define STATE_REQUESTING 2
355
356 #define TIMEOUT_INITIAL 4000
357 #define TIMEOUT_MAX 32000
358
dhcp_init_ifc(const char * ifname)359 int dhcp_init_ifc(const char *ifname)
360 {
361 dhcp_msg discover_msg;
362 dhcp_msg request_msg;
363 dhcp_msg reply;
364 dhcp_msg *msg;
365 dhcp_info info;
366 int s, r, size;
367 int valid_reply;
368 uint32_t xid;
369 unsigned char hwaddr[6];
370 struct pollfd pfd;
371 unsigned int state;
372 unsigned int timeout;
373 int if_index;
374
375 xid = (uint32_t) get_msecs();
376
377 if (if_get_hwaddr(ifname, hwaddr)) {
378 return fatal("cannot obtain interface address");
379 }
380 if ((if_index = if_nametoindex(ifname)) == 0) {
381 return fatal("cannot obtain interface index");
382 }
383
384 s = open_raw_socket(ifname, hwaddr, if_index);
385
386 timeout = TIMEOUT_INITIAL;
387 state = STATE_SELECTING;
388 info.type = 0;
389 goto transmit;
390
391 for (;;) {
392 pfd.fd = s;
393 pfd.events = POLLIN;
394 pfd.revents = 0;
395 r = poll(&pfd, 1, timeout);
396
397 if (r == 0) {
398 #if VERBOSE
399 printerr("TIMEOUT\n");
400 #endif
401 if (timeout >= TIMEOUT_MAX) {
402 printerr("timed out\n");
403 if ( info.type == DHCPOFFER ) {
404 printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
405 return dhcp_configure(ifname, &info);
406 }
407 errno = ETIME;
408 close(s);
409 return -1;
410 }
411 timeout = timeout * 2;
412
413 transmit:
414 size = 0;
415 msg = NULL;
416 switch(state) {
417 case STATE_SELECTING:
418 msg = &discover_msg;
419 size = init_dhcp_discover_msg(msg, hwaddr, xid);
420 break;
421 case STATE_REQUESTING:
422 msg = &request_msg;
423 size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
424 break;
425 default:
426 r = 0;
427 }
428 if (size != 0) {
429 r = send_message(s, if_index, msg, size);
430 if (r < 0) {
431 printerr("error sending dhcp msg: %s\n", strerror(errno));
432 }
433 }
434 continue;
435 }
436
437 if (r < 0) {
438 if ((errno == EAGAIN) || (errno == EINTR)) {
439 continue;
440 }
441 return fatal("poll failed");
442 }
443
444 errno = 0;
445 r = receive_packet(s, &reply);
446 if (r < 0) {
447 if (errno != 0) {
448 printf("receive_packet failed (%d): %s", r, strerror(errno));
449 if (errno == ENETDOWN || errno == ENXIO) {
450 return -1;
451 }
452 }
453 continue;
454 }
455
456 #if VERBOSE > 1
457 dump_dhcp_msg(&reply, r);
458 #endif
459 decode_dhcp_msg(&reply, r, &info);
460
461 if (state == STATE_SELECTING) {
462 valid_reply = is_valid_reply(&discover_msg, &reply, r);
463 } else {
464 valid_reply = is_valid_reply(&request_msg, &reply, r);
465 }
466 if (!valid_reply) {
467 printerr("invalid reply\n");
468 continue;
469 }
470
471 if (verbose) dump_dhcp_info(&info);
472
473 switch(state) {
474 case STATE_SELECTING:
475 if (info.type == DHCPOFFER) {
476 state = STATE_REQUESTING;
477 timeout = TIMEOUT_INITIAL;
478 xid++;
479 goto transmit;
480 }
481 break;
482 case STATE_REQUESTING:
483 if (info.type == DHCPACK) {
484 printerr("configuring %s\n", ifname);
485 close(s);
486 return dhcp_configure(ifname, &info);
487 } else if (info.type == DHCPNAK) {
488 printerr("configuration request denied\n");
489 close(s);
490 return -1;
491 } else {
492 printerr("ignoring %s message in state %d\n",
493 dhcp_type_to_name(info.type), state);
494 }
495 break;
496 }
497 }
498 close(s);
499 return 0;
500 }
501
do_dhcp(char * iname)502 int do_dhcp(char *iname)
503 {
504 if (if_set_addr_v4(iname, 0, 32)) {
505 printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
506 return -1;
507 }
508
509 if (if_link_up(iname)) {
510 printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
511 return -1;
512 }
513
514 return dhcp_init_ifc(iname);
515 }
516