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) { 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 239 static void hex2str(char *buf, const unsigned char *array, int len) 240 { 241 int i; 242 char *cp = buf; 243 244 for (i = 0; i < len; i++) { 245 cp += sprintf(cp, " %02x ", array[i]); 246 } 247 } 248 249 void 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, msg->chaddr, c); 282 ALOGD("chaddr = {%s}", buf); 283 284 for (n = 0; n < 64; n++) { 285 if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) { 286 if (msg->sname[n] == 0) break; 287 msg->sname[n] = '.'; 288 } 289 } 290 msg->sname[63] = 0; 291 292 for (n = 0; n < 128; n++) { 293 if ((msg->file[n] < ' ') || (msg->file[n] > 127)) { 294 if (msg->file[n] == 0) break; 295 msg->file[n] = '.'; 296 } 297 } 298 msg->file[127] = 0; 299 300 ALOGD("sname = '%s'", msg->sname); 301 ALOGD("file = '%s'", msg->file); 302 303 if (len < 4) return; 304 len -= 4; 305 x = msg->options + 4; 306 307 while (len > 2) { 308 if (*x == 0) { 309 x++; 310 len--; 311 continue; 312 } 313 if (*x == OPT_END) { 314 break; 315 } 316 len -= 2; 317 optsz = x[1]; 318 if (optsz > len) break; 319 if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) { 320 if ((unsigned int)optsz < sizeof(buf) - 1) { 321 n = optsz; 322 } else { 323 n = sizeof(buf) - 1; 324 } 325 memcpy(buf, &x[2], n); 326 buf[n] = '\0'; 327 } else { 328 hex2str(buf, &x[2], optsz); 329 } 330 if (x[0] == OPT_MESSAGE_TYPE) 331 name = dhcp_type_to_name(x[2]); 332 else 333 name = NULL; 334 ALOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name); 335 len -= optsz; 336 x = x + optsz + 2; 337 } 338 } 339 340 #endif 341 342 static int send_message(int sock, int if_index, dhcp_msg *msg, int size) 343 { 344 #if VERBOSE > 1 345 dump_dhcp_msg(msg, size); 346 #endif 347 return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST, 348 PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER); 349 } 350 351 static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) 352 { 353 if (sz < DHCP_MSG_FIXED_SIZE) { 354 if (verbose) ALOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE); 355 return 0; 356 } 357 if (reply->op != OP_BOOTREPLY) { 358 if (verbose) ALOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY); 359 return 0; 360 } 361 if (reply->xid != msg->xid) { 362 if (verbose) ALOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), 363 ntohl(msg->xid)); 364 return 0; 365 } 366 if (reply->htype != msg->htype) { 367 if (verbose) ALOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype); 368 return 0; 369 } 370 if (reply->hlen != msg->hlen) { 371 if (verbose) ALOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen); 372 return 0; 373 } 374 if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) { 375 if (verbose) ALOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr)); 376 return 0; 377 } 378 return 1; 379 } 380 381 #define STATE_SELECTING 1 382 #define STATE_REQUESTING 2 383 384 #define TIMEOUT_INITIAL 4000 385 #define TIMEOUT_MAX 32000 386 387 int dhcp_init_ifc(const char *ifname) 388 { 389 dhcp_msg discover_msg; 390 dhcp_msg request_msg; 391 dhcp_msg reply; 392 dhcp_msg *msg; 393 dhcp_info info; 394 int s, r, size; 395 int valid_reply; 396 uint32_t xid; 397 unsigned char hwaddr[6]; 398 struct pollfd pfd; 399 unsigned int state; 400 unsigned int timeout; 401 int if_index; 402 403 xid = (uint32_t) get_msecs(); 404 405 if (ifc_get_hwaddr(ifname, hwaddr)) { 406 return fatal("cannot obtain interface address"); 407 } 408 if (ifc_get_ifindex(ifname, &if_index)) { 409 return fatal("cannot obtain interface index"); 410 } 411 412 s = open_raw_socket(ifname, hwaddr, if_index); 413 414 timeout = TIMEOUT_INITIAL; 415 state = STATE_SELECTING; 416 info.type = 0; 417 goto transmit; 418 419 for (;;) { 420 pfd.fd = s; 421 pfd.events = POLLIN; 422 pfd.revents = 0; 423 r = poll(&pfd, 1, timeout); 424 425 if (r == 0) { 426 #if VERBOSE 427 printerr("TIMEOUT\n"); 428 #endif 429 if (timeout >= TIMEOUT_MAX) { 430 printerr("timed out\n"); 431 if ( info.type == DHCPOFFER ) { 432 printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname); 433 return dhcp_configure(ifname, &info); 434 } 435 errno = ETIME; 436 close(s); 437 return -1; 438 } 439 timeout = timeout * 2; 440 441 transmit: 442 size = 0; 443 msg = NULL; 444 switch(state) { 445 case STATE_SELECTING: 446 msg = &discover_msg; 447 size = init_dhcp_discover_msg(msg, hwaddr, xid); 448 break; 449 case STATE_REQUESTING: 450 msg = &request_msg; 451 size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr); 452 break; 453 default: 454 r = 0; 455 } 456 if (size != 0) { 457 r = send_message(s, if_index, msg, size); 458 if (r < 0) { 459 printerr("error sending dhcp msg: %s\n", strerror(errno)); 460 } 461 } 462 continue; 463 } 464 465 if (r < 0) { 466 if ((errno == EAGAIN) || (errno == EINTR)) { 467 continue; 468 } 469 return fatal("poll failed"); 470 } 471 472 errno = 0; 473 r = receive_packet(s, &reply); 474 if (r < 0) { 475 if (errno != 0) { 476 ALOGD("receive_packet failed (%d): %s", r, strerror(errno)); 477 if (errno == ENETDOWN || errno == ENXIO) { 478 return -1; 479 } 480 } 481 continue; 482 } 483 484 #if VERBOSE > 1 485 dump_dhcp_msg(&reply, r); 486 #endif 487 decode_dhcp_msg(&reply, r, &info); 488 489 if (state == STATE_SELECTING) { 490 valid_reply = is_valid_reply(&discover_msg, &reply, r); 491 } else { 492 valid_reply = is_valid_reply(&request_msg, &reply, r); 493 } 494 if (!valid_reply) { 495 printerr("invalid reply\n"); 496 continue; 497 } 498 499 if (verbose) dump_dhcp_info(&info); 500 501 switch(state) { 502 case STATE_SELECTING: 503 if (info.type == DHCPOFFER) { 504 state = STATE_REQUESTING; 505 timeout = TIMEOUT_INITIAL; 506 xid++; 507 goto transmit; 508 } 509 break; 510 case STATE_REQUESTING: 511 if (info.type == DHCPACK) { 512 printerr("configuring %s\n", ifname); 513 close(s); 514 return dhcp_configure(ifname, &info); 515 } else if (info.type == DHCPNAK) { 516 printerr("configuration request denied\n"); 517 close(s); 518 return -1; 519 } else { 520 printerr("ignoring %s message in state %d\n", 521 dhcp_type_to_name(info.type), state); 522 } 523 break; 524 } 525 } 526 close(s); 527 return 0; 528 } 529 530 int do_dhcp(char *iname) 531 { 532 if (ifc_set_addr(iname, 0)) { 533 printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno)); 534 return -1; 535 } 536 537 if (ifc_up(iname)) { 538 printerr("failed to bring up interface %s: %s\n", iname, strerror(errno)); 539 return -1; 540 } 541 542 return dhcp_init_ifc(iname); 543 } 544