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