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
17void dhcp_init(struct daemon *daemon)
18{
19  int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
20  struct sockaddr_in saddr;
21  int oneopt = 1, zeroopt = 0;
22  struct dhcp_config *configs, *cp;
23
24  if (fd == -1)
25    die ("cannot create DHCP socket : %s", NULL);
26
27  if (
28#if defined(IP_PKTINFO)
29      setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
30#elif defined(IP_RECVIF)
31      setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
32#endif
33      setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
34    die("failed to set options on DHCP socket: %s", NULL);
35
36  saddr.sin_family = AF_INET;
37  saddr.sin_port = htons(DHCP_SERVER_PORT);
38  saddr.sin_addr.s_addr = INADDR_ANY;
39#ifdef HAVE_SOCKADDR_SA_LEN
40  saddr.sin_len = sizeof(struct sockaddr_in);
41#endif
42
43  if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
44    die("failed to bind DHCP server socket: %s", NULL);
45
46  daemon->dhcpfd = fd;
47
48  if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
49      setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
50      setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
51    die("cannot create ICMP raw socket: %s.", NULL);
52
53  daemon->dhcp_icmp_fd = fd;
54
55#ifdef HAVE_BPF
56  {
57    int i = 0;
58    while (1)
59      {
60	char filename[50];
61	sprintf(filename, "/dev/bpf%d", i++);
62	if ((fd = open(filename, O_RDWR, 0)) != -1)
63	  break;
64	if (errno != EBUSY)
65	  die("cannot create DHCP BPF socket: %s", NULL);
66      }
67  }
68#else
69  /* since we don't ever use the packet socket for reception,
70     and it receives copies of _all_ IP packets, then that data
71     will build up in kernel buffers, wasting memory. Set the
72     socket receive buffer size to one to avoid that. (zero is
73     rejected as non-sensical by some BSD kernels) */
74  if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
75      setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
76    die("cannot create DHCP packet socket: %s. "
77	"Is CONFIG_PACKET enabled in your kernel?", NULL);
78#endif
79
80  daemon->dhcp_raw_fd = fd;
81
82  /* If the same IP appears in more than one host config, then DISCOVER
83     for one of the hosts will get the address, but REQUEST will be NAKed,
84     since the address is reserved by the other one -> protocol loop. */
85  for (configs = daemon->dhcp_conf; configs; configs = configs->next)
86    for (cp = configs->next; cp; cp = cp->next)
87      if ((configs->flags & cp->flags & CONFIG_ADDR) &&	configs->addr.s_addr == cp->addr.s_addr)
88	die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
89
90  daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
91  /* These two each hold a DHCP option max size 256
92     and get a terminating zero added */
93  daemon->dhcp_buff = safe_malloc(257);
94  daemon->dhcp_buff2 = safe_malloc(257);
95
96}
97
98void dhcp_packet(struct daemon *daemon, time_t now)
99{
100  struct udp_dhcp_packet *rawpacket = daemon->dhcp_packet;
101  struct dhcp_packet *mess = &rawpacket->data;
102  struct dhcp_context *context;
103  struct iname *tmp;
104  struct ifreq ifr;
105  struct msghdr msg;
106  struct iovec iov[2];
107  struct cmsghdr *cmptr;
108  int sz, newlen, iface_index = 0;
109  struct in_addr iface_netmask, iface_addr, iface_broadcast;
110#ifdef HAVE_BPF
111  unsigned char iface_hwaddr[ETHER_ADDR_LEN];
112#endif
113
114  union {
115    struct cmsghdr align; /* this ensures alignment */
116#ifdef IP_PKTINFO
117    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
118#else
119    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
120#endif
121  } control_u;
122
123  iov[0].iov_base = (char *)mess;
124  iov[0].iov_len = sizeof(struct dhcp_packet);
125
126  msg.msg_control = control_u.control;
127  msg.msg_controllen = sizeof(control_u);
128  msg.msg_flags = 0;
129  msg.msg_name = NULL;
130  msg.msg_namelen = 0;
131  msg.msg_iov = iov;
132  msg.msg_iovlen = 1;
133
134  sz = recvmsg(daemon->dhcpfd, &msg, 0);
135
136  if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
137    return;
138
139#if defined (IP_PKTINFO)
140  if (msg.msg_controllen < sizeof(struct cmsghdr))
141    return;
142  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
143    if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
144      iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
145
146  if (!(ifr.ifr_ifindex = iface_index) ||
147      ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
148    return;
149
150#elif defined(IP_RECVIF)
151  if (msg.msg_controllen < sizeof(struct cmsghdr))
152    return;
153  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
154    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
155      iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
156
157  if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
158    return;
159
160#else
161  {
162    struct iname *name;
163    for (name = daemon->if_names; names->isloop; names = names->next);
164    strcpy(ifr.ifr_name, name->name);
165  }
166#endif
167
168#ifdef HAVE_BPF
169  ifr.ifr_addr.sa_family = AF_LINK;
170  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0)
171    return;
172  memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
173#endif
174
175  ifr.ifr_addr.sa_family = AF_INET;
176  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
177    return;
178  iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
179
180  /* enforce available interface configuration */
181  for (tmp = daemon->if_except; tmp; tmp = tmp->next)
182    if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
183      return;
184
185  if (daemon->if_names || daemon->if_addrs)
186    {
187      for (tmp = daemon->if_names; tmp; tmp = tmp->next)
188	if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
189	  break;
190      if (!tmp)
191	for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
192	  if (tmp->addr.sa.sa_family == AF_INET &&
193	      tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
194	    break;
195      if (!tmp)
196	return;
197    }
198
199  iface_netmask.s_addr = 0;
200  iface_broadcast.s_addr = 0;
201
202  for (context = daemon->dhcp; context; context = context->next)
203    {
204      /* Fill in missing netmask and broadcast address values for any approriate
205	 dhcp-ranges which match this interface and don't have them. */
206      if (!context->netmask.s_addr)
207	{
208	  if (!iface_netmask.s_addr && ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
209	    iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
210
211	  if (iface_netmask.s_addr &&
212	      (is_same_net(iface_addr, context->start, iface_netmask) ||
213	       is_same_net(iface_addr, context->end, iface_netmask)))
214	    {
215	      context->netmask = iface_netmask;
216	      if (!(is_same_net(iface_addr, context->start, iface_netmask) &&
217		    is_same_net(iface_addr, context->end, iface_netmask)))
218		{
219		   strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
220		   strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
221		   syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
222			  daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask));
223		}
224	    }
225	}
226
227      /* Determine "default" default routes. These are to this server or the relay agent.
228	 Also broadcast addresses, if not specified */
229      if (context->netmask.s_addr)
230	{
231	  if (is_same_net(iface_addr, context->start, context->netmask))
232	    {
233	      if (!context->router.s_addr)
234		context->router = iface_addr;
235	      if (!context->broadcast.s_addr)
236		{
237		  if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
238		    iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
239		  if (iface_broadcast.s_addr &&
240		      is_same_net(iface_broadcast, context->start, context->netmask))
241		    context->broadcast = iface_broadcast;
242		  else
243		    context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
244		}
245	    }
246	  else if (mess->giaddr.s_addr && is_same_net(mess->giaddr, context->start, context->netmask))
247	    {
248	      if (!context->router.s_addr)
249		context->router = mess->giaddr;
250	      /* fill in missing broadcast addresses for relayed ranges */
251	      if (!context->broadcast.s_addr)
252		context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
253	    }
254	}
255    }
256
257  lease_prune(NULL, now); /* lose any expired leases */
258  newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now);
259  lease_update_file(0, now);
260  lease_update_dns();
261
262  if (newlen == 0)
263    return;
264
265  if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
266    {
267      /* To send to BOOTP relay or configured client, use the IP packet */
268
269      struct sockaddr_in dest;
270      dest.sin_family = AF_INET;
271#ifdef HAVE_SOCKADDR_SA_LEN
272      dest.sin_len = sizeof(struct sockaddr_in);
273#endif
274
275      if (mess->giaddr.s_addr)
276	{
277	  dest.sin_port = htons(DHCP_SERVER_PORT);
278	  dest.sin_addr = mess->giaddr;
279	}
280      else
281	{
282	  dest.sin_port = htons(DHCP_CLIENT_PORT);
283	  dest.sin_addr = mess->ciaddr;
284	}
285
286      sendto(daemon->dhcpfd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
287    }
288  else
289    {
290      /* Hairy stuff, packet either has to go to the
291	 net broadcast or the destination can't reply to ARP yet,
292	 but we do know the physical address.
293	 Build the packet by steam, and send directly, bypassing
294	 the kernel IP stack */
295
296      u32 i, sum;
297      unsigned char hwdest[ETHER_ADDR_LEN];
298
299      if (ntohs(mess->flags) & 0x8000)
300	{
301	  memset(hwdest, 255,  ETHER_ADDR_LEN);
302	  rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
303	}
304      else
305	{
306	  memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
307	  rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
308	}
309
310      rawpacket->ip.ip_p = IPPROTO_UDP;
311      rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
312      rawpacket->ip.ip_len = htons(sizeof(struct ip) +
313				   sizeof(struct udphdr) +
314				   newlen) ;
315      rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
316      rawpacket->ip.ip_v = IPVERSION;
317      rawpacket->ip.ip_tos = 0;
318      rawpacket->ip.ip_id = htons(0);
319      rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
320      rawpacket->ip.ip_ttl = IPDEFTTL;
321      rawpacket->ip.ip_sum = 0;
322      for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
323	sum += ((u16 *)&rawpacket->ip)[i];
324      while (sum>>16)
325	sum = (sum & 0xffff) + (sum >> 16);
326      rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
327
328      rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
329      rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
330      ((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
331      rawpacket->udp.uh_sum = 0;
332      rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
333      sum += htons(IPPROTO_UDP);
334      for (i = 0; i < 4; i++)
335	sum += ((u16 *)&rawpacket->ip.ip_src)[i];
336      for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
337	sum += ((u16 *)&rawpacket->udp)[i];
338      while (sum>>16)
339	sum = (sum & 0xffff) + (sum >> 16);
340      rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
341
342      {
343#ifdef HAVE_BPF
344	struct ether_header header;
345
346	header.ether_type = htons(ETHERTYPE_IP);
347	memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
348	memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
349
350	ioctl(daemon->dhcp_raw_fd, BIOCSETIF, &ifr);
351
352	iov[0].iov_base = (char *)&header;
353	iov[0].iov_len = sizeof(struct ether_header);
354	iov[1].iov_base = (char *)rawpacket;
355	iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
356	writev(daemon->dhcp_raw_fd, iov, 2);
357#else
358	struct sockaddr_ll dest;
359
360	dest.sll_family = AF_PACKET;
361	dest.sll_halen =  ETHER_ADDR_LEN;
362	dest.sll_ifindex = iface_index;
363	dest.sll_protocol = htons(ETHERTYPE_IP);
364	memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
365	sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
366	       0, (struct sockaddr *)&dest, sizeof(dest));
367
368#endif
369      }
370    }
371}
372
373int address_available(struct dhcp_context *context, struct in_addr taddr)
374{
375  /* Check is an address is OK for this network, check all
376     possible ranges. */
377
378  unsigned int start, end, addr = ntohl(taddr.s_addr);
379
380  for (; context; context = context->current)
381    {
382      start = ntohl(context->start.s_addr);
383      end = ntohl(context->end.s_addr);
384
385      if (!context->static_only &&
386	  addr >= start &&
387	  addr <= end)
388	return 1;
389    }
390
391  return 0;
392}
393
394struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
395{
396  struct dhcp_config *config;
397
398  for (config = configs; config; config = config->next)
399    if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
400      return config;
401
402  return NULL;
403}
404
405int address_allocate(struct dhcp_context *context, struct daemon *daemon,
406		     struct in_addr *addrp, unsigned char *hwaddr)
407{
408  /* Find a free address: exclude anything in use and anything allocated to
409     a particular hwaddr/clientid/hostname in our configuration */
410
411  struct in_addr start, addr ;
412  unsigned int i, j;
413
414  for (; context; context = context->current)
415    if (!context->static_only)
416      {
417	/* pick a seed based on hwaddr then iterate until we find a free address. */
418	for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
419	  j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
420
421	start.s_addr = addr.s_addr =
422	  htonl(ntohl(context->start.s_addr) +
423		(j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
424
425	do {
426	  if (!lease_find_by_addr(addr) &&
427	      !config_find_by_address(daemon->dhcp_conf, addr))
428	    {
429	      if (icmp_ping(daemon, addr))
430		/* perturb address selection so that we are
431		   less likely to try this address again. */
432		context->addr_epoch++;
433	      else
434		{
435		  *addrp = addr;
436		  return 1;
437		}
438	    }
439
440	  addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
441
442	  if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1))
443	    addr = context->start;
444
445	} while (addr.s_addr != start.s_addr);
446      }
447  return 0;
448}
449
450static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
451{
452  if (!context)
453    return 1;
454  if (!(config->flags & CONFIG_ADDR))
455    return 1;
456  if (is_same_net(config->addr, context->start, context->netmask))
457    return 1;
458
459  return 0;
460}
461
462struct dhcp_config *find_config(struct dhcp_config *configs,
463				struct dhcp_context *context,
464				unsigned char *clid, int clid_len,
465				unsigned char *hwaddr, char *hostname)
466{
467  struct dhcp_config *config;
468
469  if (clid_len)
470    for (config = configs; config; config = config->next)
471      if (config->flags & CONFIG_CLID)
472	{
473	  if (config->clid_len == clid_len &&
474	      memcmp(config->clid, clid, clid_len) == 0 &&
475	      is_addr_in_context(context, config))
476	    return config;
477
478	  /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
479	     cope with that here */
480	  if (*clid == 0 && config->clid_len == clid_len-1  &&
481	      memcmp(config->clid, clid+1, clid_len-1) == 0 &&
482	      is_addr_in_context(context, config))
483	    return config;
484	}
485
486  for (config = configs; config; config = config->next)
487    if ((config->flags & CONFIG_HWADDR) &&
488	memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
489	is_addr_in_context(context, config))
490      return config;
491
492  if (hostname)
493    for (config = configs; config; config = config->next)
494      if ((config->flags & CONFIG_NAME) &&
495	  hostname_isequal(config->hostname, hostname) &&
496	  is_addr_in_context(context, config))
497	return config;
498
499  return NULL;
500}
501
502void dhcp_read_ethers(struct daemon *daemon)
503{
504  FILE *f = fopen(ETHERSFILE, "r");
505  unsigned int flags, e0, e1, e2, e3, e4, e5;
506  char *buff = daemon->namebuff;
507  char *ip, *cp;
508  struct in_addr addr;
509  unsigned char hwaddr[ETHER_ADDR_LEN];
510  struct dhcp_config *config, *configs = daemon->dhcp_conf;
511  int count = 0;
512
513  if (!f)
514    {
515      syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
516      return;
517    }
518
519  while (fgets(buff, MAXDNAME, f))
520    {
521      while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
522	buff[strlen(buff)-1] = 0;
523
524      if ((*buff == '#') || (*buff == '+'))
525	continue;
526
527      for (ip = buff; *ip && !isspace(*ip); ip++);
528      for(; *ip && isspace(*ip); ip++)
529	*ip = 0;
530      if (!*ip)
531	continue;
532
533      if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
534	continue;
535
536      hwaddr[0] = e0;
537      hwaddr[1] = e1;
538      hwaddr[2] = e2;
539      hwaddr[3] = e3;
540      hwaddr[4] = e4;
541      hwaddr[5] = e5;
542
543      /* check for name or dotted-quad */
544      for (cp = ip; *cp; cp++)
545	if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
546	  break;
547
548      if (!*cp)
549	{
550	  if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
551	    continue;
552	  flags = CONFIG_ADDR;
553
554	  for (config = configs; config; config = config->next)
555	    if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
556	      break;
557	}
558      else
559	{
560	  if (!canonicalise(ip))
561	    continue;
562	  flags = CONFIG_NAME;
563
564	  for (config = configs; config; config = config->next)
565	    if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
566	      break;
567	}
568
569      if (!config)
570	{
571	  for (config = configs; config; config = config->next)
572	    if ((config->flags & CONFIG_HWADDR) &&
573		memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
574	      break;
575
576	  if (!config)
577	    {
578	      if (!(config = malloc(sizeof(struct dhcp_config))))
579		continue;
580	      config->flags = 0;
581	      config->next = configs;
582	      configs = config;
583	    }
584
585	  config->flags |= flags;
586
587	  if (flags & CONFIG_NAME)
588	    {
589	      if ((config->hostname = malloc(strlen(ip)+1)))
590		strcpy(config->hostname, ip);
591	      else
592		config->flags &= ~CONFIG_NAME;
593	    }
594
595	  if (flags & CONFIG_ADDR)
596	    config->addr = addr;
597	}
598
599      config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
600      memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
601
602      count++;
603    }
604
605  fclose(f);
606
607  syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
608
609  daemon->dhcp_conf =  configs;
610}
611
612void dhcp_update_configs(struct dhcp_config *configs)
613{
614  /* Some people like to keep all static IP addresses in /etc/hosts.
615     This goes through /etc/hosts and sets static addresses for any DHCP config
616     records which don't have an address and whose name matches. */
617
618  struct dhcp_config *config;
619  struct crec *crec;
620
621  for (config = configs; config; config = config->next)
622    if (!(config->flags & CONFIG_ADDR) &&
623	(config->flags & CONFIG_NAME) &&
624	(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
625	(crec->flags & F_HOSTS))
626      {
627	config->addr = crec->addr.addr.addr4;
628	config->flags |= CONFIG_ADDR;
629      }
630}
631
632