Annotation of embedaddon/dnsmasq/src/dhcp.c, revision 1.1
1.1 ! misho 1: /* dnsmasq is Copyright (c) 2000-2013 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, or
! 6: (at your option) version 3 dated 29 June, 2007.
! 7:
! 8: This program is distributed in the hope that it will be useful,
! 9: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 10: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 11: GNU General Public License for more details.
! 12:
! 13: You should have received a copy of the GNU General Public License
! 14: along with this program. If not, see <http://www.gnu.org/licenses/>.
! 15: */
! 16:
! 17: #include "dnsmasq.h"
! 18:
! 19: #ifdef HAVE_DHCP
! 20:
! 21: struct iface_param {
! 22: struct dhcp_context *current;
! 23: int ind;
! 24: };
! 25:
! 26: struct match_param {
! 27: int ind, matched;
! 28: struct in_addr netmask, broadcast, addr;
! 29: };
! 30:
! 31: static int complete_context(struct in_addr local, int if_index,
! 32: struct in_addr netmask, struct in_addr broadcast, void *vparam);
! 33: static int check_listen_addrs(struct in_addr local, int if_index,
! 34: struct in_addr netmask, struct in_addr broadcast, void *vparam);
! 35:
! 36: static int make_fd(int port)
! 37: {
! 38: int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
! 39: struct sockaddr_in saddr;
! 40: int oneopt = 1;
! 41: #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
! 42: int mtu = IP_PMTUDISC_DONT;
! 43: #endif
! 44: #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
! 45: int tos = IPTOS_CLASS_CS6;
! 46: #endif
! 47:
! 48: if (fd == -1)
! 49: die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
! 50:
! 51: if (!fix_fd(fd) ||
! 52: #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
! 53: setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
! 54: #endif
! 55: #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
! 56: setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
! 57: #endif
! 58: #if defined(HAVE_LINUX_NETWORK)
! 59: setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
! 60: #else
! 61: setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
! 62: #endif
! 63: setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
! 64: die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
! 65:
! 66: /* When bind-interfaces is set, there might be more than one dnmsasq
! 67: instance binding port 67. That's OK if they serve different networks.
! 68: Need to set REUSEADDR|REUSEPORT to make this posible.
! 69: Handle the case that REUSEPORT is defined, but the kernel doesn't
! 70: support it. This handles the introduction of REUSEPORT on Linux. */
! 71: if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
! 72: {
! 73: int rc = -1, porterr = 0;
! 74:
! 75: #ifdef SO_REUSEPORT
! 76: if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
! 77: errno != ENOPROTOOPT)
! 78: porterr = 1;
! 79: #endif
! 80:
! 81: if (rc == -1 && !porterr)
! 82: rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
! 83:
! 84: if (rc == -1)
! 85: die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
! 86: }
! 87:
! 88: memset(&saddr, 0, sizeof(saddr));
! 89: saddr.sin_family = AF_INET;
! 90: saddr.sin_port = htons(port);
! 91: saddr.sin_addr.s_addr = INADDR_ANY;
! 92: #ifdef HAVE_SOCKADDR_SA_LEN
! 93: saddr.sin_len = sizeof(struct sockaddr_in);
! 94: #endif
! 95:
! 96: if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
! 97: die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
! 98:
! 99: return fd;
! 100: }
! 101:
! 102: void dhcp_init(void)
! 103: {
! 104: #if defined(HAVE_BSD_NETWORK)
! 105: int oneopt = 1;
! 106: #endif
! 107:
! 108: daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
! 109: if (daemon->enable_pxe)
! 110: daemon->pxefd = make_fd(PXE_PORT);
! 111: else
! 112: daemon->pxefd = -1;
! 113:
! 114: #if defined(HAVE_BSD_NETWORK)
! 115: /* When we're not using capabilities, we need to do this here before
! 116: we drop root. Also, set buffer size small, to avoid wasting
! 117: kernel buffers */
! 118:
! 119: if (option_bool(OPT_NO_PING))
! 120: daemon->dhcp_icmp_fd = -1;
! 121: else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
! 122: setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
! 123: die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
! 124:
! 125: /* Make BPF raw send socket */
! 126: init_bpf();
! 127: #endif
! 128: }
! 129:
! 130: void dhcp_packet(time_t now, int pxe_fd)
! 131: {
! 132: int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
! 133: struct dhcp_packet *mess;
! 134: struct dhcp_context *context;
! 135: struct iname *tmp;
! 136: struct ifreq ifr;
! 137: struct msghdr msg;
! 138: struct sockaddr_in dest;
! 139: struct cmsghdr *cmptr;
! 140: struct iovec iov;
! 141: ssize_t sz;
! 142: int iface_index = 0, unicast_dest = 0, is_inform = 0;
! 143: struct in_addr iface_addr;
! 144: struct iface_param parm;
! 145: #ifdef HAVE_LINUX_NETWORK
! 146: struct arpreq arp_req;
! 147: #endif
! 148:
! 149: union {
! 150: struct cmsghdr align; /* this ensures alignment */
! 151: #if defined(HAVE_LINUX_NETWORK)
! 152: char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
! 153: #elif defined(HAVE_SOLARIS_NETWORK)
! 154: char control[CMSG_SPACE(sizeof(unsigned int))];
! 155: #elif defined(HAVE_BSD_NETWORK)
! 156: char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
! 157: #endif
! 158: } control_u;
! 159: struct dhcp_bridge *bridge, *alias;
! 160:
! 161: msg.msg_controllen = sizeof(control_u);
! 162: msg.msg_control = control_u.control;
! 163: msg.msg_name = &dest;
! 164: msg.msg_namelen = sizeof(dest);
! 165: msg.msg_iov = &daemon->dhcp_packet;
! 166: msg.msg_iovlen = 1;
! 167:
! 168: if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
! 169: (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
! 170: return;
! 171:
! 172: #if defined (HAVE_LINUX_NETWORK)
! 173: if (msg.msg_controllen >= sizeof(struct cmsghdr))
! 174: for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
! 175: if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
! 176: {
! 177: union {
! 178: unsigned char *c;
! 179: struct in_pktinfo *p;
! 180: } p;
! 181: p.c = CMSG_DATA(cmptr);
! 182: iface_index = p.p->ipi_ifindex;
! 183: if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
! 184: unicast_dest = 1;
! 185: }
! 186:
! 187: #elif defined(HAVE_BSD_NETWORK)
! 188: if (msg.msg_controllen >= sizeof(struct cmsghdr))
! 189: for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
! 190: if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
! 191: {
! 192: union {
! 193: unsigned char *c;
! 194: struct sockaddr_dl *s;
! 195: } p;
! 196: p.c = CMSG_DATA(cmptr);
! 197: iface_index = p.s->sdl_index;
! 198: }
! 199:
! 200: #elif defined(HAVE_SOLARIS_NETWORK)
! 201: if (msg.msg_controllen >= sizeof(struct cmsghdr))
! 202: for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
! 203: if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
! 204: {
! 205: union {
! 206: unsigned char *c;
! 207: unsigned int *i;
! 208: } p;
! 209: p.c = CMSG_DATA(cmptr);
! 210: iface_index = *(p.i);
! 211: }
! 212: #endif
! 213:
! 214: if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
! 215: return;
! 216:
! 217: #ifdef HAVE_LINUX_NETWORK
! 218: /* ARP fiddling uses original interface even if we pretend to use a different one. */
! 219: strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
! 220: #endif
! 221:
! 222: /* One form of bridging on BSD has the property that packets
! 223: can be recieved on bridge interfaces which do not have an IP address.
! 224: We allow these to be treated as aliases of another interface which does have
! 225: an IP address with --dhcp-bridge=interface,alias,alias */
! 226: for (bridge = daemon->bridges; bridge; bridge = bridge->next)
! 227: {
! 228: for (alias = bridge->alias; alias; alias = alias->next)
! 229: if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
! 230: {
! 231: if (!(iface_index = if_nametoindex(bridge->iface)))
! 232: {
! 233: my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
! 234: return;
! 235: }
! 236: else
! 237: {
! 238: strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
! 239: break;
! 240: }
! 241: }
! 242:
! 243: if (alias)
! 244: break;
! 245: }
! 246:
! 247: #ifdef MSG_BCAST
! 248: /* OpenBSD tells us when a packet was broadcast */
! 249: if (!(msg.msg_flags & MSG_BCAST))
! 250: unicast_dest = 1;
! 251: #endif
! 252:
! 253: ifr.ifr_addr.sa_family = AF_INET;
! 254: if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
! 255: iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
! 256: else
! 257: {
! 258: my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
! 259: return;
! 260: }
! 261:
! 262: for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
! 263: if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
! 264: return;
! 265:
! 266: /* unlinked contexts are marked by context->current == context */
! 267: for (context = daemon->dhcp; context; context = context->next)
! 268: context->current = context;
! 269:
! 270: parm.current = NULL;
! 271: parm.ind = iface_index;
! 272:
! 273: if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
! 274: {
! 275: /* If we failed to match the primary address of the interface, see if we've got a --listen-address
! 276: for a secondary */
! 277: struct match_param match;
! 278:
! 279: match.matched = 0;
! 280: match.ind = iface_index;
! 281:
! 282: if (!daemon->if_addrs ||
! 283: !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
! 284: !match.matched)
! 285: return;
! 286:
! 287: iface_addr = match.addr;
! 288: /* make sure secondary address gets priority in case
! 289: there is more than one address on the interface in the same subnet */
! 290: complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
! 291: }
! 292:
! 293: if (!iface_enumerate(AF_INET, &parm, complete_context))
! 294: return;
! 295:
! 296: lease_prune(NULL, now); /* lose any expired leases */
! 297: iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
! 298: now, unicast_dest, &is_inform, pxe_fd, iface_addr);
! 299: lease_update_file(now);
! 300: lease_update_dns(0);
! 301:
! 302: if (iov.iov_len == 0)
! 303: return;
! 304:
! 305: msg.msg_name = &dest;
! 306: msg.msg_namelen = sizeof(dest);
! 307: msg.msg_control = NULL;
! 308: msg.msg_controllen = 0;
! 309: msg.msg_iov = &iov;
! 310: iov.iov_base = daemon->dhcp_packet.iov_base;
! 311:
! 312: /* packet buffer may have moved */
! 313: mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
! 314:
! 315: #ifdef HAVE_SOCKADDR_SA_LEN
! 316: dest.sin_len = sizeof(struct sockaddr_in);
! 317: #endif
! 318:
! 319: if (pxe_fd)
! 320: {
! 321: if (mess->ciaddr.s_addr != 0)
! 322: dest.sin_addr = mess->ciaddr;
! 323: }
! 324: else if (mess->giaddr.s_addr)
! 325: {
! 326: /* Send to BOOTP relay */
! 327: dest.sin_port = htons(daemon->dhcp_server_port);
! 328: dest.sin_addr = mess->giaddr;
! 329: }
! 330: else if (mess->ciaddr.s_addr)
! 331: {
! 332: /* If the client's idea of its own address tallys with
! 333: the source address in the request packet, we believe the
! 334: source port too, and send back to that. If we're replying
! 335: to a DHCPINFORM, trust the source address always. */
! 336: if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
! 337: dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
! 338: {
! 339: dest.sin_port = htons(daemon->dhcp_client_port);
! 340: dest.sin_addr = mess->ciaddr;
! 341: }
! 342: }
! 343: #if defined(HAVE_LINUX_NETWORK)
! 344: else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
! 345: mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
! 346: {
! 347: /* broadcast to 255.255.255.255 (or mac address invalid) */
! 348: struct in_pktinfo *pkt;
! 349: msg.msg_control = control_u.control;
! 350: msg.msg_controllen = sizeof(control_u);
! 351: cmptr = CMSG_FIRSTHDR(&msg);
! 352: pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
! 353: pkt->ipi_ifindex = iface_index;
! 354: pkt->ipi_spec_dst.s_addr = 0;
! 355: msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
! 356: cmptr->cmsg_level = IPPROTO_IP;
! 357: cmptr->cmsg_type = IP_PKTINFO;
! 358: dest.sin_addr.s_addr = INADDR_BROADCAST;
! 359: dest.sin_port = htons(daemon->dhcp_client_port);
! 360: }
! 361: else
! 362: {
! 363: /* unicast to unconfigured client. Inject mac address direct into ARP cache.
! 364: struct sockaddr limits size to 14 bytes. */
! 365: dest.sin_addr = mess->yiaddr;
! 366: dest.sin_port = htons(daemon->dhcp_client_port);
! 367: memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
! 368: arp_req.arp_ha.sa_family = mess->htype;
! 369: memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
! 370: /* interface name already copied in */
! 371: arp_req.arp_flags = ATF_COM;
! 372: ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
! 373: }
! 374: #elif defined(HAVE_SOLARIS_NETWORK)
! 375: else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
! 376: {
! 377: /* broadcast to 255.255.255.255 (or mac address invalid) */
! 378: dest.sin_addr.s_addr = INADDR_BROADCAST;
! 379: dest.sin_port = htons(daemon->dhcp_client_port);
! 380: /* note that we don't specify the interface here: that's done by the
! 381: IP_BOUND_IF sockopt lower down. */
! 382: }
! 383: else
! 384: {
! 385: /* unicast to unconfigured client. Inject mac address direct into ARP cache.
! 386: Note that this only works for ethernet on solaris, because we use SIOCSARP
! 387: and not SIOCSXARP, which would be perfect, except that it returns ENXIO
! 388: mysteriously. Bah. Fall back to broadcast for other net types. */
! 389: struct arpreq req;
! 390: dest.sin_addr = mess->yiaddr;
! 391: dest.sin_port = htons(daemon->dhcp_client_port);
! 392: *((struct sockaddr_in *)&req.arp_pa) = dest;
! 393: req.arp_ha.sa_family = AF_UNSPEC;
! 394: memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
! 395: req.arp_flags = ATF_COM;
! 396: ioctl(daemon->dhcpfd, SIOCSARP, &req);
! 397: }
! 398: #elif defined(HAVE_BSD_NETWORK)
! 399: else
! 400: {
! 401: send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
! 402: return;
! 403: }
! 404: #endif
! 405:
! 406: #ifdef HAVE_SOLARIS_NETWORK
! 407: setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
! 408: #endif
! 409:
! 410: while(sendmsg(fd, &msg, 0) == -1 && retry_send());
! 411: }
! 412:
! 413: /* check against secondary interface addresses */
! 414: static int check_listen_addrs(struct in_addr local, int if_index,
! 415: struct in_addr netmask, struct in_addr broadcast, void *vparam)
! 416: {
! 417: struct match_param *param = vparam;
! 418: struct iname *tmp;
! 419:
! 420: if (if_index == param->ind)
! 421: {
! 422: for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
! 423: if ( tmp->addr.sa.sa_family == AF_INET &&
! 424: tmp->addr.in.sin_addr.s_addr == local.s_addr)
! 425: {
! 426: param->matched = 1;
! 427: param->addr = local;
! 428: param->netmask = netmask;
! 429: param->broadcast = broadcast;
! 430: break;
! 431: }
! 432: }
! 433:
! 434: return 1;
! 435: }
! 436:
! 437: /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
! 438: of each interface (and any relay address) and does the following things:
! 439:
! 440: 1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
! 441: 2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
! 442: 3) Fills in local (this host) and router (this host or relay) addresses.
! 443: 4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
! 444:
! 445: Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
! 446:
! 447: static int complete_context(struct in_addr local, int if_index,
! 448: struct in_addr netmask, struct in_addr broadcast, void *vparam)
! 449: {
! 450: struct dhcp_context *context;
! 451: struct iface_param *param = vparam;
! 452:
! 453: for (context = daemon->dhcp; context; context = context->next)
! 454: {
! 455: if (!(context->flags & CONTEXT_NETMASK) &&
! 456: (is_same_net(local, context->start, netmask) ||
! 457: is_same_net(local, context->end, netmask)))
! 458: {
! 459: if (context->netmask.s_addr != netmask.s_addr &&
! 460: !(is_same_net(local, context->start, netmask) &&
! 461: is_same_net(local, context->end, netmask)))
! 462: {
! 463: strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
! 464: strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
! 465: my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
! 466: daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
! 467: }
! 468: context->netmask = netmask;
! 469: }
! 470:
! 471: if (context->netmask.s_addr != 0 &&
! 472: is_same_net(local, context->start, context->netmask) &&
! 473: is_same_net(local, context->end, context->netmask))
! 474: {
! 475: /* link it onto the current chain if we've not seen it before */
! 476: if (if_index == param->ind && context->current == context)
! 477: {
! 478: context->router = local;
! 479: context->local = local;
! 480: context->current = param->current;
! 481: param->current = context;
! 482: }
! 483:
! 484: if (!(context->flags & CONTEXT_BRDCAST))
! 485: {
! 486: if (is_same_net(broadcast, context->start, context->netmask))
! 487: context->broadcast = broadcast;
! 488: else
! 489: context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
! 490: }
! 491: }
! 492: }
! 493:
! 494: return 1;
! 495: }
! 496:
! 497: struct dhcp_context *address_available(struct dhcp_context *context,
! 498: struct in_addr taddr,
! 499: struct dhcp_netid *netids)
! 500: {
! 501: /* Check is an address is OK for this network, check all
! 502: possible ranges. Make sure that the address isn't in use
! 503: by the server itself. */
! 504:
! 505: unsigned int start, end, addr = ntohl(taddr.s_addr);
! 506: struct dhcp_context *tmp;
! 507:
! 508: for (tmp = context; tmp; tmp = tmp->current)
! 509: if (taddr.s_addr == context->router.s_addr)
! 510: return NULL;
! 511:
! 512: for (tmp = context; tmp; tmp = tmp->current)
! 513: {
! 514: start = ntohl(tmp->start.s_addr);
! 515: end = ntohl(tmp->end.s_addr);
! 516:
! 517: if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
! 518: addr >= start &&
! 519: addr <= end &&
! 520: match_netid(tmp->filter, netids, 1))
! 521: return tmp;
! 522: }
! 523:
! 524: return NULL;
! 525: }
! 526:
! 527: struct dhcp_context *narrow_context(struct dhcp_context *context,
! 528: struct in_addr taddr,
! 529: struct dhcp_netid *netids)
! 530: {
! 531: /* We start of with a set of possible contexts, all on the current physical interface.
! 532: These are chained on ->current.
! 533: Here we have an address, and return the actual context correponding to that
! 534: address. Note that none may fit, if the address came a dhcp-host and is outside
! 535: any dhcp-range. In that case we return a static range if possible, or failing that,
! 536: any context on the correct subnet. (If there's more than one, this is a dodgy
! 537: configuration: maybe there should be a warning.) */
! 538:
! 539: struct dhcp_context *tmp;
! 540:
! 541: if (!(tmp = address_available(context, taddr, netids)))
! 542: {
! 543: for (tmp = context; tmp; tmp = tmp->current)
! 544: if (match_netid(tmp->filter, netids, 1) &&
! 545: is_same_net(taddr, tmp->start, tmp->netmask) &&
! 546: (tmp->flags & CONTEXT_STATIC))
! 547: break;
! 548:
! 549: if (!tmp)
! 550: for (tmp = context; tmp; tmp = tmp->current)
! 551: if (match_netid(tmp->filter, netids, 1) &&
! 552: is_same_net(taddr, tmp->start, tmp->netmask) &&
! 553: !(tmp->flags & CONTEXT_PROXY))
! 554: break;
! 555: }
! 556:
! 557: /* Only one context allowed now */
! 558: if (tmp)
! 559: tmp->current = NULL;
! 560:
! 561: return tmp;
! 562: }
! 563:
! 564: struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
! 565: {
! 566: struct dhcp_config *config;
! 567:
! 568: for (config = configs; config; config = config->next)
! 569: if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
! 570: return config;
! 571:
! 572: return NULL;
! 573: }
! 574:
! 575: int address_allocate(struct dhcp_context *context,
! 576: struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
! 577: struct dhcp_netid *netids, time_t now)
! 578: {
! 579: /* Find a free address: exclude anything in use and anything allocated to
! 580: a particular hwaddr/clientid/hostname in our configuration.
! 581: Try to return from contexts which match netids first. */
! 582:
! 583: struct in_addr start, addr;
! 584: struct dhcp_context *c, *d;
! 585: int i, pass;
! 586: unsigned int j;
! 587:
! 588: /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
! 589: dispersal even with similarly-valued "strings". */
! 590: for (j = 0, i = 0; i < hw_len; i++)
! 591: j += hwaddr[i] + (j << 6) + (j << 16) - j;
! 592:
! 593: for (pass = 0; pass <= 1; pass++)
! 594: for (c = context; c; c = c->current)
! 595: if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
! 596: continue;
! 597: else if (!match_netid(c->filter, netids, pass))
! 598: continue;
! 599: else
! 600: {
! 601: if (option_bool(OPT_CONSEC_ADDR))
! 602: /* seed is largest extant lease addr in this context */
! 603: start = lease_find_max_addr(c);
! 604: else
! 605: /* pick a seed based on hwaddr */
! 606: start.s_addr = htonl(ntohl(c->start.s_addr) +
! 607: ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
! 608:
! 609: /* iterate until we find a free address. */
! 610: addr = start;
! 611:
! 612: do {
! 613: /* eliminate addresses in use by the server. */
! 614: for (d = context; d; d = d->current)
! 615: if (addr.s_addr == d->router.s_addr)
! 616: break;
! 617:
! 618: /* Addresses which end in .255 and .0 are broken in Windows even when using
! 619: supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
! 620: then 192.168.0.255 is a valid IP address, but not for Windows as it's
! 621: in the class C range. See KB281579. We therefore don't allocate these
! 622: addresses to avoid hard-to-diagnose problems. Thanks Bill. */
! 623: if (!d &&
! 624: !lease_find_by_addr(addr) &&
! 625: !config_find_by_address(daemon->dhcp_conf, addr) &&
! 626: (!IN_CLASSC(ntohl(addr.s_addr)) ||
! 627: ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
! 628: {
! 629: struct ping_result *r, *victim = NULL;
! 630: int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
! 631: ((float)PING_WAIT)));
! 632:
! 633: *addrp = addr;
! 634:
! 635: /* check if we failed to ping addr sometime in the last
! 636: PING_CACHE_TIME seconds. If so, assume the same situation still exists.
! 637: This avoids problems when a stupid client bangs
! 638: on us repeatedly. As a final check, if we did more
! 639: than 60% of the possible ping checks in the last
! 640: PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
! 641: for (count = 0, r = daemon->ping_results; r; r = r->next)
! 642: if (difftime(now, r->time) > (float)PING_CACHE_TIME)
! 643: victim = r; /* old record */
! 644: else
! 645: {
! 646: count++;
! 647: if (r->addr.s_addr == addr.s_addr)
! 648: {
! 649: /* consec-ip mode: we offered this address for another client
! 650: (different hash) recently, don't offer it to this one. */
! 651: if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
! 652: break;
! 653:
! 654: return 1;
! 655: }
! 656: }
! 657:
! 658: if (!r)
! 659: {
! 660: if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
! 661: {
! 662: /* address in use: perturb address selection so that we are
! 663: less likely to try this address again. */
! 664: if (!option_bool(OPT_CONSEC_ADDR))
! 665: c->addr_epoch++;
! 666: }
! 667: else
! 668: {
! 669: /* at this point victim may hold an expired record */
! 670: if (!victim)
! 671: {
! 672: if ((victim = whine_malloc(sizeof(struct ping_result))))
! 673: {
! 674: victim->next = daemon->ping_results;
! 675: daemon->ping_results = victim;
! 676: }
! 677: }
! 678:
! 679: /* record that this address is OK for 30s
! 680: without more ping checks */
! 681: if (victim)
! 682: {
! 683: victim->addr = addr;
! 684: victim->time = now;
! 685: victim->hash = j;
! 686: }
! 687: return 1;
! 688: }
! 689: }
! 690: }
! 691:
! 692: addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
! 693:
! 694: if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
! 695: addr = c->start;
! 696:
! 697: } while (addr.s_addr != start.s_addr);
! 698: }
! 699:
! 700: return 0;
! 701: }
! 702:
! 703: static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
! 704: {
! 705: if (!context) /* called via find_config() from lease_update_from_configs() */
! 706: return 1;
! 707: if (!(config->flags & CONFIG_ADDR))
! 708: return 1;
! 709: for (; context; context = context->current)
! 710: if (is_same_net(config->addr, context->start, context->netmask))
! 711: return 1;
! 712:
! 713: return 0;
! 714: }
! 715:
! 716: int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
! 717: {
! 718: struct hwaddr_config *conf_addr;
! 719:
! 720: for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
! 721: if (conf_addr->wildcard_mask == 0 &&
! 722: conf_addr->hwaddr_len == len &&
! 723: (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
! 724: memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
! 725: return 1;
! 726:
! 727: return 0;
! 728: }
! 729:
! 730: struct dhcp_config *find_config(struct dhcp_config *configs,
! 731: struct dhcp_context *context,
! 732: unsigned char *clid, int clid_len,
! 733: unsigned char *hwaddr, int hw_len,
! 734: int hw_type, char *hostname)
! 735: {
! 736: int count, new;
! 737: struct dhcp_config *config, *candidate;
! 738: struct hwaddr_config *conf_addr;
! 739:
! 740: if (clid)
! 741: for (config = configs; config; config = config->next)
! 742: if (config->flags & CONFIG_CLID)
! 743: {
! 744: if (config->clid_len == clid_len &&
! 745: memcmp(config->clid, clid, clid_len) == 0 &&
! 746: is_addr_in_context(context, config))
! 747: return config;
! 748:
! 749: /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
! 750: cope with that here */
! 751: if (*clid == 0 && config->clid_len == clid_len-1 &&
! 752: memcmp(config->clid, clid+1, clid_len-1) == 0 &&
! 753: is_addr_in_context(context, config))
! 754: return config;
! 755: }
! 756:
! 757:
! 758: for (config = configs; config; config = config->next)
! 759: if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
! 760: is_addr_in_context(context, config))
! 761: return config;
! 762:
! 763: if (hostname && context)
! 764: for (config = configs; config; config = config->next)
! 765: if ((config->flags & CONFIG_NAME) &&
! 766: hostname_isequal(config->hostname, hostname) &&
! 767: is_addr_in_context(context, config))
! 768: return config;
! 769:
! 770: /* use match with fewest wildcard octets */
! 771: for (candidate = NULL, count = 0, config = configs; config; config = config->next)
! 772: if (is_addr_in_context(context, config))
! 773: for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
! 774: if (conf_addr->wildcard_mask != 0 &&
! 775: conf_addr->hwaddr_len == hw_len &&
! 776: (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
! 777: (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
! 778: {
! 779: count = new;
! 780: candidate = config;
! 781: }
! 782:
! 783: return candidate;
! 784: }
! 785:
! 786: void dhcp_read_ethers(void)
! 787: {
! 788: FILE *f = fopen(ETHERSFILE, "r");
! 789: unsigned int flags;
! 790: char *buff = daemon->namebuff;
! 791: char *ip, *cp;
! 792: struct in_addr addr;
! 793: unsigned char hwaddr[ETHER_ADDR_LEN];
! 794: struct dhcp_config **up, *tmp;
! 795: struct dhcp_config *config;
! 796: int count = 0, lineno = 0;
! 797:
! 798: addr.s_addr = 0; /* eliminate warning */
! 799:
! 800: if (!f)
! 801: {
! 802: my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
! 803: return;
! 804: }
! 805:
! 806: /* This can be called again on SIGHUP, so remove entries created last time round. */
! 807: for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
! 808: {
! 809: tmp = config->next;
! 810: if (config->flags & CONFIG_FROM_ETHERS)
! 811: {
! 812: *up = tmp;
! 813: /* cannot have a clid */
! 814: if (config->flags & CONFIG_NAME)
! 815: free(config->hostname);
! 816: free(config->hwaddr);
! 817: free(config);
! 818: }
! 819: else
! 820: up = &config->next;
! 821: }
! 822:
! 823: while (fgets(buff, MAXDNAME, f))
! 824: {
! 825: char *host = NULL;
! 826:
! 827: lineno++;
! 828:
! 829: while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
! 830: buff[strlen(buff)-1] = 0;
! 831:
! 832: if ((*buff == '#') || (*buff == '+') || (*buff == 0))
! 833: continue;
! 834:
! 835: for (ip = buff; *ip && !isspace((int)*ip); ip++);
! 836: for(; *ip && isspace((int)*ip); ip++)
! 837: *ip = 0;
! 838: if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
! 839: {
! 840: my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
! 841: continue;
! 842: }
! 843:
! 844: /* check for name or dotted-quad */
! 845: for (cp = ip; *cp; cp++)
! 846: if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
! 847: break;
! 848:
! 849: if (!*cp)
! 850: {
! 851: if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
! 852: {
! 853: my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
! 854: continue;
! 855: }
! 856:
! 857: flags = CONFIG_ADDR;
! 858:
! 859: for (config = daemon->dhcp_conf; config; config = config->next)
! 860: if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
! 861: break;
! 862: }
! 863: else
! 864: {
! 865: int nomem;
! 866: if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
! 867: {
! 868: if (!nomem)
! 869: my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
! 870: free(host);
! 871: continue;
! 872: }
! 873:
! 874: flags = CONFIG_NAME;
! 875:
! 876: for (config = daemon->dhcp_conf; config; config = config->next)
! 877: if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
! 878: break;
! 879: }
! 880:
! 881: if (config && (config->flags & CONFIG_FROM_ETHERS))
! 882: {
! 883: my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
! 884: continue;
! 885: }
! 886:
! 887: if (!config)
! 888: {
! 889: for (config = daemon->dhcp_conf; config; config = config->next)
! 890: {
! 891: struct hwaddr_config *conf_addr = config->hwaddr;
! 892: if (conf_addr &&
! 893: conf_addr->next == NULL &&
! 894: conf_addr->wildcard_mask == 0 &&
! 895: conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
! 896: (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
! 897: memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
! 898: break;
! 899: }
! 900:
! 901: if (!config)
! 902: {
! 903: if (!(config = whine_malloc(sizeof(struct dhcp_config))))
! 904: continue;
! 905: config->flags = CONFIG_FROM_ETHERS;
! 906: config->hwaddr = NULL;
! 907: config->domain = NULL;
! 908: config->netid = NULL;
! 909: config->next = daemon->dhcp_conf;
! 910: daemon->dhcp_conf = config;
! 911: }
! 912:
! 913: config->flags |= flags;
! 914:
! 915: if (flags & CONFIG_NAME)
! 916: {
! 917: config->hostname = host;
! 918: host = NULL;
! 919: }
! 920:
! 921: if (flags & CONFIG_ADDR)
! 922: config->addr = addr;
! 923: }
! 924:
! 925: config->flags |= CONFIG_NOCLID;
! 926: if (!config->hwaddr)
! 927: config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
! 928: if (config->hwaddr)
! 929: {
! 930: memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
! 931: config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
! 932: config->hwaddr->hwaddr_type = ARPHRD_ETHER;
! 933: config->hwaddr->wildcard_mask = 0;
! 934: config->hwaddr->next = NULL;
! 935: }
! 936: count++;
! 937:
! 938: free(host);
! 939:
! 940: }
! 941:
! 942: fclose(f);
! 943:
! 944: my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
! 945: }
! 946:
! 947:
! 948: /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
! 949: for this address. If it has a domain part, that must match the set domain and
! 950: it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
! 951: so check here that the domain name is legal as a hostname.
! 952: NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
! 953: char *host_from_dns(struct in_addr addr)
! 954: {
! 955: struct crec *lookup;
! 956:
! 957: if (daemon->port == 0)
! 958: return NULL; /* DNS disabled. */
! 959:
! 960: lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
! 961:
! 962: if (lookup && (lookup->flags & F_HOSTS))
! 963: {
! 964: char *dot, *hostname = cache_get_name(lookup);
! 965: dot = strchr(hostname, '.');
! 966:
! 967: if (dot && strlen(dot+1) != 0)
! 968: {
! 969: char *d2 = get_domain(addr);
! 970: if (!d2 || !hostname_isequal(dot+1, d2))
! 971: return NULL; /* wrong domain */
! 972: }
! 973:
! 974: if (!legal_hostname(hostname))
! 975: return NULL;
! 976:
! 977: strncpy(daemon->dhcp_buff, hostname, 256);
! 978: daemon->dhcp_buff[255] = 0;
! 979: strip_hostname(daemon->dhcp_buff);
! 980:
! 981: return daemon->dhcp_buff;
! 982: }
! 983:
! 984: return NULL;
! 985: }
! 986:
! 987: #endif
! 988:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>