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 45 static int verbose = 1; 46 static char errmsg[2048]; 47 48 typedef unsigned long long msecs_t; 49 #if VERBOSE 50 void dump_dhcp_msg(); 51 #endif 52 53 msecs_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 65 void 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 76 const char *dhcp_lasterror() 77 { 78 return errmsg; 79 } 80 81 int fatal(const char *reason) 82 { 83 printerr("%s: %s\n", reason, strerror(errno)); 84 return -1; 85 // exit(1); 86 } 87 88 const 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 96 extern int ipv4NetmaskToPrefixLength(in_addr_t mask); 97 98 typedef struct dhcp_info dhcp_info; 99 100 struct 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 114 dhcp_info last_good_info; 115 116 void 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 129 static 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 136 static 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 151 void dump_dhcp_info(dhcp_info *info) 152 { 153 char addr[20], gway[20], mask[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 166 int 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) info->prefixLength = ipv4NetmaskToPrefixLength(*((uint32_t*)x)); 201 break; 202 case OPT_GATEWAY: 203 if (optlen >= 4) memcpy(&info->gateway, x, 4); 204 break; 205 case OPT_DNS: 206 if (optlen >= 4) memcpy(&info->dns1, x + 0, 4); 207 if (optlen >= 8) memcpy(&info->dns2, x + 4, 4); 208 break; 209 case OPT_LEASE_TIME: 210 if (optlen >= 4) { 211 memcpy(&info->lease, x, 4); 212 info->lease = ntohl(info->lease); 213 } 214 break; 215 case OPT_SERVER_ID: 216 if (optlen >= 4) memcpy(&info->serveraddr, x, 4); 217 break; 218 case OPT_MESSAGE_TYPE: 219 info->type = *x; 220 break; 221 default: 222 break; 223 } 224 x += optlen; 225 len -= optlen; 226 } 227 228 info->ipaddr = msg->yiaddr; 229 230 return 0; 231 } 232 233 #if VERBOSE 234 235 static void hex2str(char *buf, const unsigned char *array, int len) 236 { 237 int i; 238 char *cp = buf; 239 240 for (i = 0; i < len; i++) { 241 cp += sprintf(cp, " %02x ", array[i]); 242 } 243 } 244 245 void dump_dhcp_msg(dhcp_msg *msg, int len) 246 { 247 unsigned char *x; 248 unsigned int n,c; 249 int optsz; 250 const char *name; 251 char buf[2048]; 252 253 ALOGD("===== DHCP message:"); 254 if (len < DHCP_MSG_FIXED_SIZE) { 255 ALOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE); 256 return; 257 } 258 259 len -= DHCP_MSG_FIXED_SIZE; 260 261 if (msg->op == OP_BOOTREQUEST) 262 name = "BOOTREQUEST"; 263 else if (msg->op == OP_BOOTREPLY) 264 name = "BOOTREPLY"; 265 else 266 name = "????"; 267 ALOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d", 268 name, msg->op, msg->htype, msg->hlen, msg->hops); 269 ALOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d", 270 ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len); 271 ALOGD("ciaddr = %s", ipaddr(msg->ciaddr)); 272 ALOGD("yiaddr = %s", ipaddr(msg->yiaddr)); 273 ALOGD("siaddr = %s", ipaddr(msg->siaddr)); 274 ALOGD("giaddr = %s", ipaddr(msg->giaddr)); 275 276 c = msg->hlen > 16 ? 16 : msg->hlen; 277 hex2str(buf, msg->chaddr, c); 278 ALOGD("chaddr = {%s}", buf); 279 280 for (n = 0; n < 64; n++) { 281 if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) { 282 if (msg->sname[n] == 0) break; 283 msg->sname[n] = '.'; 284 } 285 } 286 msg->sname[63] = 0; 287 288 for (n = 0; n < 128; n++) { 289 if ((msg->file[n] < ' ') || (msg->file[n] > 127)) { 290 if (msg->file[n] == 0) break; 291 msg->file[n] = '.'; 292 } 293 } 294 msg->file[127] = 0; 295 296 ALOGD("sname = '%s'", msg->sname); 297 ALOGD("file = '%s'", msg->file); 298 299 if (len < 4) return; 300 len -= 4; 301 x = msg->options + 4; 302 303 while (len > 2) { 304 if (*x == 0) { 305 x++; 306 len--; 307 continue; 308 } 309 if (*x == OPT_END) { 310 break; 311 } 312 len -= 2; 313 optsz = x[1]; 314 if (optsz > len) break; 315 if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) { 316 if ((unsigned int)optsz < sizeof(buf) - 1) { 317 n = optsz; 318 } else { 319 n = sizeof(buf) - 1; 320 } 321 memcpy(buf, &x[2], n); 322 buf[n] = '\0'; 323 } else { 324 hex2str(buf, &x[2], optsz); 325 } 326 if (x[0] == OPT_MESSAGE_TYPE) 327 name = dhcp_type_to_name(x[2]); 328 else 329 name = NULL; 330 ALOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name); 331 len -= optsz; 332 x = x + optsz + 2; 333 } 334 } 335 336 #endif 337 338 static int send_message(int sock, int if_index, dhcp_msg *msg, int size) 339 { 340 #if VERBOSE > 1 341 dump_dhcp_msg(msg, size); 342 #endif 343 return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST, 344 PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER); 345 } 346 347 static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) 348 { 349 if (sz < DHCP_MSG_FIXED_SIZE) { 350 if (verbose) ALOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE); 351 return 0; 352 } 353 if (reply->op != OP_BOOTREPLY) { 354 if (verbose) ALOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY); 355 return 0; 356 } 357 if (reply->xid != msg->xid) { 358 if (verbose) ALOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), 359 ntohl(msg->xid)); 360 return 0; 361 } 362 if (reply->htype != msg->htype) { 363 if (verbose) ALOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype); 364 return 0; 365 } 366 if (reply->hlen != msg->hlen) { 367 if (verbose) ALOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen); 368 return 0; 369 } 370 if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) { 371 if (verbose) ALOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr)); 372 return 0; 373 } 374 return 1; 375 } 376 377 #define STATE_SELECTING 1 378 #define STATE_REQUESTING 2 379 380 #define TIMEOUT_INITIAL 4000 381 #define TIMEOUT_MAX 32000 382 383 int dhcp_init_ifc(const char *ifname) 384 { 385 dhcp_msg discover_msg; 386 dhcp_msg request_msg; 387 dhcp_msg reply; 388 dhcp_msg *msg; 389 dhcp_info info; 390 int s, r, size; 391 int valid_reply; 392 uint32_t xid; 393 unsigned char hwaddr[6]; 394 struct pollfd pfd; 395 unsigned int state; 396 unsigned int timeout; 397 int if_index; 398 399 xid = (uint32_t) get_msecs(); 400 401 if (ifc_get_hwaddr(ifname, hwaddr)) { 402 return fatal("cannot obtain interface address"); 403 } 404 if (ifc_get_ifindex(ifname, &if_index)) { 405 return fatal("cannot obtain interface index"); 406 } 407 408 s = open_raw_socket(ifname, hwaddr, if_index); 409 410 timeout = TIMEOUT_INITIAL; 411 state = STATE_SELECTING; 412 info.type = 0; 413 goto transmit; 414 415 for (;;) { 416 pfd.fd = s; 417 pfd.events = POLLIN; 418 pfd.revents = 0; 419 r = poll(&pfd, 1, timeout); 420 421 if (r == 0) { 422 #if VERBOSE 423 printerr("TIMEOUT\n"); 424 #endif 425 if (timeout >= TIMEOUT_MAX) { 426 printerr("timed out\n"); 427 if ( info.type == DHCPOFFER ) { 428 printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname); 429 return dhcp_configure(ifname, &info); 430 } 431 errno = ETIME; 432 close(s); 433 return -1; 434 } 435 timeout = timeout * 2; 436 437 transmit: 438 size = 0; 439 msg = NULL; 440 switch(state) { 441 case STATE_SELECTING: 442 msg = &discover_msg; 443 size = init_dhcp_discover_msg(msg, hwaddr, xid); 444 break; 445 case STATE_REQUESTING: 446 msg = &request_msg; 447 size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr); 448 break; 449 default: 450 r = 0; 451 } 452 if (size != 0) { 453 r = send_message(s, if_index, msg, size); 454 if (r < 0) { 455 printerr("error sending dhcp msg: %s\n", strerror(errno)); 456 } 457 } 458 continue; 459 } 460 461 if (r < 0) { 462 if ((errno == EAGAIN) || (errno == EINTR)) { 463 continue; 464 } 465 return fatal("poll failed"); 466 } 467 468 errno = 0; 469 r = receive_packet(s, &reply); 470 if (r < 0) { 471 if (errno != 0) { 472 ALOGD("receive_packet failed (%d): %s", r, strerror(errno)); 473 if (errno == ENETDOWN || errno == ENXIO) { 474 return -1; 475 } 476 } 477 continue; 478 } 479 480 #if VERBOSE > 1 481 dump_dhcp_msg(&reply, r); 482 #endif 483 decode_dhcp_msg(&reply, r, &info); 484 485 if (state == STATE_SELECTING) { 486 valid_reply = is_valid_reply(&discover_msg, &reply, r); 487 } else { 488 valid_reply = is_valid_reply(&request_msg, &reply, r); 489 } 490 if (!valid_reply) { 491 printerr("invalid reply\n"); 492 continue; 493 } 494 495 if (verbose) dump_dhcp_info(&info); 496 497 switch(state) { 498 case STATE_SELECTING: 499 if (info.type == DHCPOFFER) { 500 state = STATE_REQUESTING; 501 timeout = TIMEOUT_INITIAL; 502 xid++; 503 goto transmit; 504 } 505 break; 506 case STATE_REQUESTING: 507 if (info.type == DHCPACK) { 508 printerr("configuring %s\n", ifname); 509 close(s); 510 return dhcp_configure(ifname, &info); 511 } else if (info.type == DHCPNAK) { 512 printerr("configuration request denied\n"); 513 close(s); 514 return -1; 515 } else { 516 printerr("ignoring %s message in state %d\n", 517 dhcp_type_to_name(info.type), state); 518 } 519 break; 520 } 521 } 522 close(s); 523 return 0; 524 } 525 526 int do_dhcp(char *iname) 527 { 528 if (ifc_set_addr(iname, 0)) { 529 printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno)); 530 return -1; 531 } 532 533 if (ifc_up(iname)) { 534 printerr("failed to bring up interface %s: %s\n", iname, strerror(errno)); 535 return -1; 536 } 537 538 return dhcp_init_ifc(iname); 539 } 540