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