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