1/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11*/ 12 13/* Author's email: simon@thekelleys.org.uk */ 14 15#include "dnsmasq.h" 16 17#define BOOTREQUEST 1 18#define BOOTREPLY 2 19#define DHCP_COOKIE 0x63825363 20 21/* The Linux in-kernel DHCP client silently ignores any packet 22 smaller than this. Sigh........... */ 23#define MIN_PACKETSZ 300 24 25#define OPTION_PAD 0 26#define OPTION_NETMASK 1 27#define OPTION_ROUTER 3 28#define OPTION_DNSSERVER 6 29#define OPTION_HOSTNAME 12 30#define OPTION_DOMAINNAME 15 31#define OPTION_BROADCAST 28 32#define OPTION_REQUESTED_IP 50 33#define OPTION_LEASE_TIME 51 34#define OPTION_OVERLOAD 52 35#define OPTION_MESSAGE_TYPE 53 36#define OPTION_SERVER_IDENTIFIER 54 37#define OPTION_REQUESTED_OPTIONS 55 38#define OPTION_MESSAGE 56 39#define OPTION_MAXMESSAGE 57 40#define OPTION_T1 58 41#define OPTION_T2 59 42#define OPTION_VENDOR_ID 60 43#define OPTION_CLIENT_ID 61 44#define OPTION_USER_CLASS 77 45#define OPTION_SUBNET_SELECT 118 46#define OPTION_END 255 47 48#define DHCPDISCOVER 1 49#define DHCPOFFER 2 50#define DHCPREQUEST 3 51#define DHCPDECLINE 4 52#define DHCPACK 5 53#define DHCPNAK 6 54#define DHCPRELEASE 7 55#define DHCPINFORM 8 56 57static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val); 58static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start); 59static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string); 60static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname); 61static int option_len(unsigned char *opt); 62static void *option_ptr(unsigned char *opt); 63static struct in_addr option_addr(unsigned char *opt); 64static unsigned int option_uint(unsigned char *opt, int size); 65static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string); 66static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type); 67static unsigned char *do_req_options(struct dhcp_context *context, 68 unsigned char *p, unsigned char *end, 69 unsigned char *req_options, 70 struct daemon *daemon, 71 char *hostname, 72 struct in_addr iface_addr, 73 struct dhcp_netid *netid, 74 struct in_addr subnet_addr); 75 76static int have_config(struct dhcp_config *config, unsigned int mask) 77{ 78 return config && (config->flags & mask); 79} 80 81int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now) 82{ 83 struct dhcp_context *context, *context_tmp; 84 unsigned char *opt, *clid; 85 struct dhcp_lease *lease, *ltmp; 86 struct dhcp_vendor *vendor; 87 int clid_len; 88 struct dhcp_packet *mess = &daemon->dhcp_packet->data; 89 unsigned char *p = mess->options + sizeof(u32); /* skip cookie */ 90 unsigned char *end = (unsigned char *)(daemon->dhcp_packet + 1); 91 char *hostname = NULL; 92 char *req_options = NULL; 93 char *message = NULL; 94 unsigned int renewal_time, expires_time, def_time; 95 struct dhcp_config *config; 96 struct dhcp_netid *netid = NULL; 97 struct in_addr addr, subnet_addr; 98 unsigned short fuzz = 0; 99 unsigned int mess_type = 0; 100 101 subnet_addr.s_addr = 0; 102 103 if (mess->op != BOOTREQUEST) 104 return 0; 105 106 /* Token ring is supported when we have packet sockets 107 to make the HW headers for us. We don't have the code to build 108 token ring headers when using BPF. We rely on the fact that 109 token ring hwaddrs are the same size as ethernet hwaddrs. */ 110 111#ifdef HAVE_BPF 112 if (mess->htype != ARPHRD_ETHER) 113#else 114 if (mess->htype != ARPHRD_ETHER && mess->htype != ARPHRD_IEEE802) 115#endif 116 { 117 syslog(LOG_WARNING, "DHCP request for unsupported hardware type (%d) recieved on %s", 118 mess->htype, iface_name); 119 return 0; 120 } 121 122 if (mess->hlen != ETHER_ADDR_LEN) 123 return 0; 124 125 /* check for DHCP rather than BOOTP */ 126 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE))) 127 { 128 mess_type = option_uint(opt, 1); 129 130 /* only insist on a cookie for DHCP. */ 131 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE)) 132 return 0; 133 134 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since 135 it can affect the context-determination code. */ 136 if ((option_find(mess, sz, OPTION_REQUESTED_IP) || mess_type == DHCPDISCOVER)) 137 mess->ciaddr.s_addr = 0; 138 139 /* Check for RFC3011 subnet selector */ 140 if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT))) 141 subnet_addr = option_addr(opt); 142 } 143 144 /* Determine network for this packet. If the machine has an address already, and we don't have 145 have a giaddr or explicit subnet selector, use the ciaddr. This is necessary because a 146 machine which got a lease via a relay won't use the relay to renew. */ 147 addr = 148 subnet_addr.s_addr ? subnet_addr : 149 (mess->giaddr.s_addr ? mess->giaddr : 150 (mess->ciaddr.s_addr ? mess->ciaddr : iface_addr)); 151 152 /* More than one context may match, we build a chain of them all on ->current 153 Note that if netmasks, netid or lease times don't match, odd things may happen. */ 154 155 for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) 156 if (context_tmp->netmask.s_addr && 157 is_same_net(addr, context_tmp->start, context_tmp->netmask) && 158 is_same_net(addr, context_tmp->end, context_tmp->netmask)) 159 { 160 context_tmp->current = context; 161 context = context_tmp; 162 163 /* start to build netid chain */ 164 if (context_tmp->netid.net) 165 { 166 context_tmp->netid.next = netid; 167 netid = &context_tmp->netid; 168 } 169 } 170 171 if (!context) 172 { 173 syslog(LOG_WARNING, "no address range available for DHCP request %s %s", 174 subnet_addr.s_addr ? "with subnet selector" : "via", 175 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name)); 176 return 0; 177 } 178 179 mess->op = BOOTREPLY; 180 181 if (mess_type == 0) 182 { 183 /* BOOTP request */ 184 config = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, NULL); 185 if (have_config(config, CONFIG_ADDR) && 186 !have_config(config, CONFIG_DISABLE) && 187 !lease_find_by_addr(config->addr)) 188 { 189 struct dhcp_netid id; 190 char save = mess->file[128]; 191 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */ 192 mess->yiaddr = config->addr; 193 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr; 194 if (have_config(config, CONFIG_NAME)) 195 hostname = config->hostname; 196 if (have_config(config, CONFIG_NETID)) 197 { 198 config->netid.next = netid; 199 netid = &config->netid; 200 } 201 /* Match incoming filename field as a netid. */ 202 if (mess->file[0]) 203 { 204 mess->file[128] = 0; /* ensure zero term. */ 205 id.net = mess->file; 206 id.next = netid; 207 netid = &id; 208 } 209 p = do_req_options(context, p, end, NULL, daemon, 210 hostname, iface_addr, netid, subnet_addr); 211 /* must do this after do_req_options since it overwrites filename field. */ 212 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname); 213 p = option_end(p, end, mess); 214 log_packet(NULL, &config->addr, mess->chaddr, iface_name, NULL); 215 mess->file[128] = save; 216 return p - (unsigned char *)mess; 217 } 218 return 0; 219 } 220 221 /* If there is no client identifier option, use the hardware address */ 222 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID))) 223 { 224 clid = option_ptr(opt); 225 clid_len = option_len(opt); 226 } 227 else 228 { 229 clid = mess->chaddr; 230 clid_len = 0; 231 } 232 233 config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL); 234 235 if (have_config(config, CONFIG_NAME)) 236 hostname = config->hostname; 237 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME))) 238 { 239 int len = option_len(opt); 240 hostname = daemon->dhcp_buff; 241 memcpy(hostname, option_ptr(opt), len); 242 /* May not be zero terminated */ 243 hostname[len] = 0; 244 /* ensure there are no strange chars in there */ 245 if (!canonicalise(hostname)) 246 hostname = NULL; 247 else 248 { 249 char *dot = strchr(hostname, '.'); 250 if (dot) 251 { 252 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)) 253 { 254 syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname); 255 hostname = NULL; 256 } 257 else 258 { 259 *dot = 0; /* truncate */ 260 if (strlen(hostname) == 0) 261 hostname = NULL; /* nothing left */ 262 } 263 } 264 265 /* Search again now we have a hostname. 266 Only accept configs without CLID and HWADDR here, (they won't match) 267 to avoid impersonation by name. */ 268 if (!config) 269 { 270 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname); 271 if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR)) 272 config = new; 273 } 274 } 275 } 276 277 if (have_config(config, CONFIG_NETID)) 278 { 279 config->netid.next = netid; 280 netid = &config->netid; 281 } 282 283 /* Theres a chance that carefully chosen data could match the same 284 vendor/user option twice and make a loop in the netid chain. */ 285 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) 286 vendor->used = 0; 287 288 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID))) 289 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) 290 if (vendor->is_vendor && !vendor->used) 291 { 292 int i; 293 for (i = 0; i <= (option_len(opt) - vendor->len); i++) 294 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0) 295 { 296 vendor->used = 1; 297 vendor->netid.next = netid; 298 netid = &vendor->netid; 299 break; 300 } 301 } 302 303 if ((opt = option_find(mess, sz, OPTION_USER_CLASS))) 304 { 305 unsigned char *ucp = option_ptr(opt); 306 int j; 307 for (j = 0; j < option_len(opt); j += ucp[j] + 1) 308 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) 309 if (!vendor->is_vendor && !vendor->used) 310 { 311 int i; 312 for (i = 0; i <= (ucp[j] - vendor->len); i++) 313 if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0) 314 { 315 vendor->used = 1; 316 vendor->netid.next = netid; 317 netid = &vendor->netid; 318 break; 319 } 320 } 321 } 322 323 /* Can have setting to ignore the client ID for a particular MAC address or hostname */ 324 if (have_config(config, CONFIG_NOCLID)) 325 { 326 clid = mess->chaddr; 327 clid_len = 0; 328 } 329 330 /* do we have a lease in store? */ 331 lease = lease_find_by_client(clid, clid_len); 332 333 def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; 334 335 if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) 336 { 337 unsigned int req_time = option_uint(opt, 4); 338 339 if (def_time == 0xffffffff || 340 (req_time != 0xffffffff && req_time < def_time)) 341 expires_time = renewal_time = req_time; 342 else 343 expires_time = renewal_time = def_time; 344 } 345 else 346 { 347 renewal_time = def_time; 348 if (lease) 349 expires_time = (unsigned int)difftime(lease->expires, now); 350 else 351 expires_time = def_time; 352 } 353 354 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) 355 { 356 int len = option_len(opt); 357 req_options = daemon->dhcp_buff2; 358 memcpy(req_options, option_ptr(opt), len); 359 req_options[len] = OPTION_END; 360 } 361 362 switch (mess_type) 363 { 364 case DHCPDECLINE: 365 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) || 366 (iface_addr.s_addr != option_addr(opt).s_addr)) 367 return 0; 368 369 /* sanitise any message. Paranoid? Moi? */ 370 if ((opt = option_find(mess, sz, OPTION_MESSAGE))) 371 { 372 char *p = option_ptr(opt), *q = daemon->dhcp_buff; 373 int i; 374 375 for (i = option_len(opt); i > 0; i--) 376 { 377 char c = *p++; 378 if (isprint(c)) 379 *q++ = c; 380 } 381 *q++ = 0; /* add terminator */ 382 message = daemon->dhcp_buff; 383 } 384 385 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP))) 386 return 0; 387 388 log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message); 389 390 if (lease && lease->addr.s_addr == option_addr(opt).s_addr) 391 lease_prune(lease, now); 392 393 if (have_config(config, CONFIG_ADDR) && 394 config->addr.s_addr == option_addr(opt).s_addr) 395 { 396 syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr)); 397 config->flags &= ~CONFIG_ADDR ; 398 } 399 else 400 /* make sure this host gets a different address next time. */ 401 for (; context; context = context->current) 402 context->addr_epoch++; 403 404 return 0; 405 406 case DHCPRELEASE: 407 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) || 408 (iface_addr.s_addr != option_addr(opt).s_addr)) 409 return 0; 410 411 log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL); 412 413 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr) 414 lease_prune(lease, now); 415 416 return 0; 417 418 case DHCPDISCOVER: 419 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) 420 addr = option_addr(opt); 421 if (have_config(config, CONFIG_DISABLE)) 422 message = "ignored"; 423 else if (have_config(config, CONFIG_ADDR) && 424 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease)) 425 mess->yiaddr = config->addr; 426 else if (lease && address_available(context, lease->addr)) 427 mess->yiaddr = lease->addr; 428 else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) && 429 !config_find_by_address(daemon->dhcp_conf, addr)) 430 mess->yiaddr = addr; 431 else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr)) 432 message = "no address available"; 433 log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message); 434 435 if (message) 436 return 0; 437 438 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname); 439 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr; 440 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER); 441 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); 442 p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time); 443 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */ 444 if (expires_time != 0xffffffff) 445 { 446 p = option_put(p, end, OPTION_T1, 4, (expires_time/2)); 447 p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8)); 448 } 449 p = do_req_options(context, p, end, req_options, daemon, 450 NULL, iface_addr, netid, subnet_addr); 451 p = option_end(p, end, mess); 452 453 log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL); 454 return p - (unsigned char *)mess; 455 456 case DHCPREQUEST: 457 if (have_config(config, CONFIG_DISABLE)) 458 message = "disabled"; 459 else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) 460 { 461 /* SELECTING or INIT_REBOOT */ 462 mess->yiaddr = option_addr(opt); 463 464 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER))) 465 { 466 /* SELECTING */ 467 if (iface_addr.s_addr != option_addr(opt).s_addr) 468 return 0; 469 470 /* If a lease exists for this host and another address, squash it. */ 471 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) 472 { 473 lease_prune(lease, now); 474 lease = NULL; 475 } 476 477 if (!lease) 478 { 479 if (lease_find_by_addr(mess->yiaddr)) 480 message = "address in use"; 481 else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr))) 482 message = "no leases left"; 483 } 484 } 485 else 486 { 487 /* INIT-REBOOT */ 488 if (!lease) 489 return 0; 490 491 if (lease->addr.s_addr != mess->yiaddr.s_addr) 492 message = "wrong address"; 493 } 494 } 495 else 496 { 497 /* RENEWING or REBINDING */ 498 /* Must exist a lease for this address */ 499 if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr) 500 message = "lease not found"; 501 502 /* desynchronise renewals */ 503 fuzz = rand16(); 504 while (fuzz > (renewal_time/16)) 505 fuzz = fuzz/2; 506 507 mess->yiaddr = mess->ciaddr; 508 } 509 510 if (!message) 511 { 512 struct dhcp_config *addr_config; 513 /* If a machine moves networks whilst it has a lease, we catch that here. */ 514 if (!is_same_net(mess->yiaddr, context->start, context->netmask)) 515 message = "wrong network"; 516 517 /* Check for renewal of a lease which is now outside the allowed range. */ 518 else if (!address_available(context, mess->yiaddr) && 519 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr)) 520 message = "address no longer available"; 521 522 /* Check if a new static address has been configured. Be very sure that 523 when the client does DISCOVER, it will get the static address, otherwise 524 an endless protocol loop will ensue. */ 525 526 else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr)) 527 message = "static lease available"; 528 529 /* Check to see if the address is reserved as a static address for another host */ 530 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config) 531 message ="address reserved"; 532 } 533 534 log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL); 535 536 if (message) 537 { 538 log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message); 539 540 mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0; 541 bootp_option_put(mess, NULL, NULL); 542 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK); 543 p = option_put_string(p, end, OPTION_MESSAGE, message); 544 p = option_end(p, end, mess); 545 mess->flags |= htons(0x8000); /* broadcast */ 546 return p - (unsigned char *)mess; 547 } 548 549 log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname); 550 551 lease_set_hwaddr(lease, mess->chaddr); 552 if (hostname) 553 lease_set_hostname(lease, hostname, daemon->domain_suffix); 554 lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time); 555 556 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname); 557 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr; 558 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); 559 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); 560 p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time); 561 if (renewal_time != 0xffffffff) 562 { 563 p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz); 564 p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz); 565 } 566 p = do_req_options(context, p, end, req_options, daemon, 567 hostname, iface_addr, netid, subnet_addr); 568 p = option_end(p, end, mess); 569 return p - (unsigned char *)mess; 570 571 case DHCPINFORM: 572 if (have_config(config, CONFIG_DISABLE)) 573 message = "ignored"; 574 575 log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message); 576 577 if (message || mess->ciaddr.s_addr == 0) 578 return 0; 579 580 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); 581 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr)); 582 p = do_req_options(context, p, end, req_options, daemon, 583 hostname, iface_addr, netid, subnet_addr); 584 p = option_end(p, end, mess); 585 586 log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname); 587 return p - (unsigned char *)mess; 588 } 589 590 return 0; 591} 592 593static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string) 594{ 595 syslog(LOG_INFO, "%s%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s", 596 type ? "DHCP" : "BOOTP", 597 type ? type : "", 598 interface, 599 addr ? " " : "", 600 addr ? inet_ntoa(*addr) : "", 601 hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5], 602 string ? " " : "", 603 string ? string : ""); 604} 605 606static int option_len(unsigned char *opt) 607{ 608 return opt[1]; 609} 610 611static void *option_ptr(unsigned char *opt) 612{ 613 return &opt[2]; 614} 615 616static struct in_addr option_addr(unsigned char *opt) 617{ 618 /* this worries about unaligned data in the option. */ 619 /* struct in_addr is network byte order */ 620 struct in_addr ret; 621 622 memcpy(&ret, option_ptr(opt), INADDRSZ); 623 624 return ret; 625} 626 627static unsigned int option_uint(unsigned char *opt, int size) 628{ 629 /* this worries about unaligned data and byte order */ 630 unsigned int ret = 0; 631 int i; 632 unsigned char *p = option_ptr(opt); 633 634 for (i = 0; i < size; i++) 635 ret = (ret << 8) | *p++; 636 637 return ret; 638} 639 640static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname) 641{ 642 memset(mess->sname, 0, sizeof(mess->sname)); 643 memset(mess->file, 0, sizeof(mess->file)); 644 if (sname) 645 strncpy(mess->sname, sname, sizeof(mess->sname)-1); 646 if (filename) 647 strncpy(mess->file, filename, sizeof(mess->file)-1); 648} 649 650static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val) 651{ 652 int i; 653 654 /* always keep one octet space for the END option. */ 655 if (p + len + 3 < end) 656 { 657 *(p++) = opt; 658 *(p++) = len; 659 660 for (i = 0; i < len; i++) 661 *(p++) = val >> (8 * (len - (i + 1))); 662 } 663 return p; 664} 665 666static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start) 667{ 668 *(p++) = OPTION_END; 669 while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ)) 670 *p++ = 0; 671 672 return p; 673} 674 675static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string) 676{ 677 int len = strlen(string); 678 679 if (p + len + 3 < end) 680 { 681 *(p++) = opt; 682 *(p++) = len; 683 memcpy(p, string, len); 684 p += len; 685 } 686 687 return p; 688} 689 690static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload) 691{ 692 if (!p) 693 return NULL; 694 695 while (*p != OPTION_END) 696 { 697 if (end && (p >= end)) 698 return 0; /* malformed packet */ 699 else if (*p == OPTION_PAD) 700 p++; 701 else if (*p == OPTION_OVERLOAD) 702 { 703 if (end && (p >= end - 3)) 704 return 0; /* malformed packet */ 705 if (overload) 706 *overload = *(p+2); 707 p += 3; 708 } 709 else 710 { 711 int opt_len;; 712 if (end && (p >= end - 2)) 713 return 0; /* malformed packet */ 714 opt_len = option_len(p); 715 if (end && (p >= end - (2 + opt_len))) 716 return 0; /* malformed packet */ 717 if (*p == opt) 718 return p; 719 p += opt_len + 2; 720 } 721 } 722 723 return NULL; 724} 725 726static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type) 727{ 728 int overload = 0; 729 unsigned char *ret; 730 731 /* skip over DHCP cookie; */ 732 ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, &overload); 733 734 if (!ret && (overload & 1)) 735 ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload); 736 737 if (!ret && (overload & 2)) 738 ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload); 739 740 return ret; 741} 742 743static int in_list(unsigned char *list, int opt) 744{ 745 int i; 746 747 /* If no requested options, send everything, not nothing. */ 748 if (!list) 749 return 1; 750 751 for (i = 0; list[i] != OPTION_END; i++) 752 if (opt == list[i]) 753 return 1; 754 755 return 0; 756} 757 758static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt) 759{ 760 struct dhcp_opt *tmp; 761 struct dhcp_netid *tmp1; 762 763 for (tmp = opts; tmp; tmp = tmp->next) 764 if (tmp->opt == opt) 765 { 766 if (netid) 767 { 768 if (tmp->netid) 769 for (tmp1 = netid; tmp1; tmp1 = tmp1->next) 770 if (strcmp(tmp->netid, tmp1->net) == 0) 771 return tmp; 772 } 773 else if (!tmp->netid) 774 return tmp; 775 } 776 777 return netid ? option_find2(NULL, opts, opt) : NULL; 778} 779 780static unsigned char *do_req_options(struct dhcp_context *context, 781 unsigned char *p, unsigned char *end, 782 unsigned char *req_options, 783 struct daemon *daemon, 784 char *hostname, 785 struct in_addr iface_addr, 786 struct dhcp_netid *netid, 787 struct in_addr subnet_addr) 788{ 789 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts; 790 791 if (in_list(req_options, OPTION_MAXMESSAGE)) 792 p = option_put(p, end, OPTION_MAXMESSAGE, 2, end - (unsigned char *)daemon->dhcp_packet); 793 794 /* rfc3011 says this doesn't need to be in the requested options list. */ 795 if (subnet_addr.s_addr) 796 p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr)); 797 798 if (in_list(req_options, OPTION_NETMASK) && 799 !option_find2(netid, config_opts, OPTION_NETMASK)) 800 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr)); 801 802 /* May not have a "guessed" broadcast address if we got no packets via a relay 803 from this net yet (ie just unicast renewals after a restart */ 804 if (context->broadcast.s_addr && 805 in_list(req_options, OPTION_BROADCAST) && 806 !option_find2(netid, config_opts, OPTION_BROADCAST)) 807 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr)); 808 809 /* Same comments as broadcast apply, and also may not be able to get a sensible 810 default when using subnet select. User must configure by steam in that case. */ 811 if (context->router.s_addr && 812 in_list(req_options, OPTION_ROUTER) && 813 !option_find2(netid, config_opts, OPTION_ROUTER)) 814 p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr)); 815 816 if (in_list(req_options, OPTION_DNSSERVER) && 817 !option_find2(netid, config_opts, OPTION_DNSSERVER)) 818 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr)); 819 820 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) && 821 !option_find2(netid, config_opts, OPTION_DOMAINNAME)) 822 p = option_put_string(p, end, OPTION_DOMAINNAME, daemon->domain_suffix); 823 824 /* Note that we ignore attempts to set the hostname using 825 --dhcp-option=12,<name> */ 826 if (hostname && in_list(req_options, OPTION_HOSTNAME)) 827 p = option_put_string(p, end, OPTION_HOSTNAME, hostname); 828 829 for (opt=config_opts; opt; opt = opt->next) 830 { 831 if (opt->opt == OPTION_HOSTNAME || 832 opt->opt == OPTION_MAXMESSAGE || 833 !in_list(req_options, opt->opt) || 834 opt != option_find2(netid, config_opts, opt->opt) || 835 p + opt->len + 3 >= end) 836 continue; 837 838 /* For the options we have default values on 839 dhc-option=<optionno> means "don't include this option" 840 not "include a zero-length option" */ 841 if (opt->len == 0 && 842 (opt->opt == OPTION_NETMASK || 843 opt->opt == OPTION_BROADCAST || 844 opt->opt == OPTION_ROUTER || 845 opt->opt == OPTION_DNSSERVER)) 846 continue; 847 848 *(p++) = opt->opt; 849 *(p++) = opt->len; 850 if (opt->len == 0) 851 continue; 852 853 if (opt->is_addr) 854 { 855 int j; 856 struct in_addr *a = (struct in_addr *)opt->val; 857 for (j = 0; j < opt->len; j+=INADDRSZ, a++) 858 { 859 /* zero means "self" */ 860 if (a->s_addr == 0) 861 memcpy(p, &iface_addr, INADDRSZ); 862 else 863 memcpy(p, a, INADDRSZ); 864 p += INADDRSZ; 865 } 866 } 867 else 868 { 869 memcpy(p, opt->val, opt->len); 870 p += opt->len; 871 } 872 } 873 return p; 874} 875 876 877