Coverage Report

Created: 2025-07-09 06:38

/src/dnsmasq/src/dhcp.c
Line
Count
Source (jump to first uncovered line)
1
/* dnsmasq is Copyright (c) 2000-2025 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, char *label,
32
          struct in_addr netmask, struct in_addr broadcast, void *vparam);
33
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
34
            struct in_addr netmask, struct in_addr broadcast, void *vparam);
35
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
36
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
37
38
static int make_fd(int port)
39
0
{
40
0
  int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
41
0
  struct sockaddr_in saddr;
42
0
  int oneopt = 1;
43
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
44
0
  int mtu = IP_PMTUDISC_DONT;
45
0
#endif
46
0
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
47
0
  int tos = IPTOS_CLASS_CS6;
48
0
#endif
49
50
0
  if (fd == -1)
51
0
    die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
52
  
53
0
  if (!fix_fd(fd) ||
54
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
55
0
      setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
56
0
#endif
57
0
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
58
0
      setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
59
0
#endif
60
0
#if defined(HAVE_LINUX_NETWORK)
61
0
      setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
62
#else
63
      setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
64
#endif
65
0
      setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)  
66
0
    die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
67
  
68
  /* When bind-interfaces is set, there might be more than one dnsmasq
69
     instance binding port 67. That's OK if they serve different networks.
70
     Need to set REUSEADDR|REUSEPORT to make this possible.
71
     Handle the case that REUSEPORT is defined, but the kernel doesn't 
72
     support it. This handles the introduction of REUSEPORT on Linux. */
73
0
  if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
74
0
    {
75
0
      int rc = 0;
76
77
0
#ifdef SO_REUSEPORT
78
0
      if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && 
79
0
    errno == ENOPROTOOPT)
80
0
  rc = 0;
81
0
#endif
82
      
83
0
      if (rc != -1)
84
0
  rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
85
      
86
0
      if (rc == -1)
87
0
  die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
88
0
    }
89
  
90
0
  memset(&saddr, 0, sizeof(saddr));
91
0
  saddr.sin_family = AF_INET;
92
0
  saddr.sin_port = htons(port);
93
0
  saddr.sin_addr.s_addr = INADDR_ANY;
94
#ifdef HAVE_SOCKADDR_SA_LEN
95
  saddr.sin_len = sizeof(struct sockaddr_in);
96
#endif
97
98
0
  if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
99
0
    die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
100
101
0
  return fd;
102
0
}
103
104
void dhcp_init(void)
105
0
{
106
#if defined(HAVE_BSD_NETWORK)
107
  int oneopt = 1;
108
#endif
109
110
0
  daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
111
0
  if (daemon->enable_pxe)
112
0
    daemon->pxefd = make_fd(PXE_PORT);
113
0
  else
114
0
    daemon->pxefd = -1;
115
116
#if defined(HAVE_BSD_NETWORK)
117
  /* When we're not using capabilities, we need to do this here before
118
     we drop root. Also, set buffer size small, to avoid wasting
119
     kernel buffers */
120
  
121
  if (option_bool(OPT_NO_PING))
122
    daemon->dhcp_icmp_fd = -1;
123
  else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
124
     setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
125
    die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
126
  
127
  /* Make BPF raw send socket */
128
  init_bpf();
129
#endif  
130
0
}
131
132
void dhcp_packet(time_t now, int pxe_fd)
133
0
{
134
0
  int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
135
0
  struct dhcp_packet *mess;
136
0
  struct dhcp_context *context;
137
0
  struct dhcp_relay *relay;
138
0
  int is_relay_reply = 0, is_relay_use_source = 0;
139
0
  struct iname *tmp;
140
0
  struct ifreq ifr;
141
0
  struct msghdr msg;
142
0
  struct sockaddr_in dest;
143
0
  struct cmsghdr *cmptr;
144
0
  struct iovec iov;
145
0
  ssize_t sz; 
146
0
  int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0;
147
0
  int rcvd_iface_index;
148
0
  struct in_addr iface_addr;
149
0
  struct iface_param parm;
150
0
  time_t recvtime = now;
151
0
#ifdef HAVE_LINUX_NETWORK
152
0
  struct arpreq arp_req;
153
0
  struct timeval tv;
154
0
#endif
155
  
156
0
  union {
157
0
    struct cmsghdr align; /* this ensures alignment */
158
0
#if defined(HAVE_LINUX_NETWORK)
159
0
    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
160
#elif defined(HAVE_SOLARIS_NETWORK)
161
    char control[CMSG_SPACE(sizeof(unsigned int))];
162
#elif defined(HAVE_BSD_NETWORK) 
163
    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
164
#endif
165
0
  } control_u;
166
0
  struct dhcp_bridge *bridge, *alias;
167
168
0
  msg.msg_controllen = sizeof(control_u);
169
0
  msg.msg_control = control_u.control;
170
0
  msg.msg_name = &dest;
171
0
  msg.msg_namelen = sizeof(dest);
172
0
  msg.msg_iov = &daemon->dhcp_packet;
173
0
  msg.msg_iovlen = 1;
174
  
175
0
  if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || 
176
0
      (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) 
177
0
    return;
178
  
179
0
#ifdef HAVE_DUMPFILE
180
0
  dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
181
0
#endif
182
  
183
0
#if defined (HAVE_LINUX_NETWORK)
184
0
  if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
185
0
    recvtime = tv.tv_sec;
186
  
187
0
  if (msg.msg_controllen >= sizeof(struct cmsghdr))
188
0
    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
189
0
      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
190
0
  {
191
0
    union {
192
0
      unsigned char *c;
193
0
      struct in_pktinfo *p;
194
0
    } p;
195
0
    p.c = CMSG_DATA(cmptr);
196
0
    iface_index = p.p->ipi_ifindex;
197
0
    if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
198
0
      unicast_dest = 1;
199
0
  }
200
201
#elif defined(HAVE_BSD_NETWORK) 
202
  if (msg.msg_controllen >= sizeof(struct cmsghdr))
203
    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
204
      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
205
        {
206
    union {
207
            unsigned char *c;
208
            struct sockaddr_dl *s;
209
          } p;
210
    p.c = CMSG_DATA(cmptr);
211
    iface_index = p.s->sdl_index;
212
  }
213
  
214
#elif defined(HAVE_SOLARIS_NETWORK) 
215
  if (msg.msg_controllen >= sizeof(struct cmsghdr))
216
    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
217
      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
218
  {
219
    union {
220
      unsigned char *c;
221
      unsigned int *i;
222
    } p;
223
    p.c = CMSG_DATA(cmptr);
224
    iface_index = *(p.i);
225
  }
226
#endif
227
  
228
0
  if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
229
0
      ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
230
0
    return;
231
  
232
0
  mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
233
  
234
  /* Non-standard extension:
235
     If giaddr == 255.255.255.255 we reply to the source
236
     address in the request packet header. This makes
237
     stand-alone leasequery clients easier, as they
238
     can leave source address determination to the kernel.
239
     In this case, set a flag and clear giaddr here,
240
     to avoid massive relay confusion. */
241
0
  if (mess->giaddr.s_addr == INADDR_BROADCAST)
242
0
    {
243
0
      mess->giaddr.s_addr = 0;
244
0
      is_relay_use_source = 1;
245
0
    }
246
  
247
0
  loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
248
  
249
0
#ifdef HAVE_LINUX_NETWORK
250
  /* ARP fiddling uses original interface even if we pretend to use a different one. */
251
0
  safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
252
0
#endif 
253
254
  /* If the interface on which the DHCP request was received is an
255
     alias of some other interface (as specified by the
256
     --bridge-interface option), change ifr.ifr_name so that we look
257
     for DHCP contexts associated with the aliased interface instead
258
     of with the aliasing one. */
259
0
  rcvd_iface_index = iface_index;
260
0
  for (bridge = daemon->bridges; bridge; bridge = bridge->next)
261
0
    {
262
0
      for (alias = bridge->alias; alias; alias = alias->next)
263
0
  if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
264
0
    {
265
0
      if (!(iface_index = if_nametoindex(bridge->iface)))
266
0
        {
267
0
    my_syslog(MS_DHCP | LOG_WARNING,
268
0
        _("unknown interface %s in bridge-interface"),
269
0
        bridge->iface);
270
0
    return;
271
0
        }
272
0
      else 
273
0
        {
274
0
    safe_strncpy(ifr.ifr_name,  bridge->iface, sizeof(ifr.ifr_name));
275
0
    break;
276
0
        }
277
0
    }
278
      
279
0
      if (alias)
280
0
  break;
281
0
    }
282
283
#ifdef MSG_BCAST
284
  /* OpenBSD tells us when a packet was broadcast */
285
  if (!(msg.msg_flags & MSG_BCAST))
286
    unicast_dest = 1;
287
#endif
288
  
289
0
  if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
290
0
    {
291
      /* Reply from server, using us as relay. */
292
0
      rcvd_iface_index = relay->iface_index;
293
0
      if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
294
0
  return;
295
0
      is_relay_reply = 1; 
296
0
      iov.iov_len = sz;
297
0
#ifdef HAVE_LINUX_NETWORK
298
0
      safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
299
0
#endif 
300
0
    }
301
0
  else
302
0
    {
303
0
      ifr.ifr_addr.sa_family = AF_INET;
304
0
      if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
305
0
  iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
306
0
      else
307
0
  {
308
0
    if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
309
0
      my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
310
0
    return;
311
0
  }
312
      
313
0
      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
314
0
  if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name))
315
0
    return;
316
      
317
      /* unlinked contexts/relays are marked by context->current == context */
318
0
      for (context = daemon->dhcp; context; context = context->next)
319
0
  context->current = context;
320
      
321
0
      parm.current = NULL;
322
0
      parm.ind = iface_index;
323
      
324
0
      if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
325
0
  {
326
    /* If we failed to match the primary address of the interface, see if we've got a --listen-address
327
       for a secondary */
328
0
    struct match_param match;
329
    
330
0
    match.matched = 0;
331
0
    match.ind = iface_index;
332
    
333
0
    if (!daemon->if_addrs ||
334
0
        !iface_enumerate(AF_INET, &match, (callback_t){.af_inet=check_listen_addrs}) ||
335
0
        !match.matched)
336
0
      return;
337
    
338
0
    iface_addr = match.addr;
339
    /* make sure secondary address gets priority in case
340
       there is more than one address on the interface in the same subnet */
341
0
    complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
342
0
  }    
343
            
344
0
      if (!iface_enumerate(AF_INET, &parm, (callback_t){.af_inet=complete_context}))
345
0
  return;
346
347
0
      relay_upstream4(iface_index, mess, (size_t)sz);
348
       
349
      /* May have configured relay, but not DHCP server */
350
0
      if (!daemon->dhcp)
351
0
  return;
352
353
0
      lease_prune(NULL, now); /* lose any expired leases */
354
0
      iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, now, unicast_dest,
355
0
             loopback, &is_inform, pxe_fd, iface_addr, recvtime,
356
0
             is_relay_use_source ? dest.sin_addr : mess->giaddr);
357
0
      lease_update_file(now);
358
0
      lease_update_dns(0);
359
      
360
0
      if (iov.iov_len == 0)
361
0
  return;
362
0
    }
363
364
0
  msg.msg_name = &dest;
365
0
  msg.msg_namelen = sizeof(dest);
366
0
  msg.msg_control = NULL;
367
0
  msg.msg_controllen = 0;
368
0
  msg.msg_iov = &iov;
369
0
  iov.iov_base = daemon->dhcp_packet.iov_base;
370
  
371
  /* packet buffer may have moved */
372
0
  mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
373
  
374
#ifdef HAVE_SOCKADDR_SA_LEN
375
  dest.sin_len = sizeof(struct sockaddr_in);
376
#endif
377
  
378
0
  if (pxe_fd)
379
0
    { 
380
0
      if (mess->ciaddr.s_addr != 0)
381
0
  dest.sin_addr = mess->ciaddr;
382
0
    }
383
0
  if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
384
0
    {
385
      /* Send to BOOTP relay. */
386
0
      if (is_relay_use_source)
387
  /* restore as-received value */
388
0
  mess->giaddr.s_addr = INADDR_BROADCAST;
389
0
      else
390
0
  {
391
0
    dest.sin_addr = mess->giaddr;
392
0
    dest.sin_port = htons(daemon->dhcp_server_port);
393
0
  }
394
0
    }
395
0
  else if (mess->ciaddr.s_addr)
396
0
    {
397
      /* If the client's idea of its own address tallys with
398
   the source address in the request packet, we believe the
399
   source port too, and send back to that.  If we're replying 
400
   to a DHCPINFORM, trust the source address always. */
401
0
      if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
402
0
    dest.sin_port == 0 || dest.sin_addr.s_addr == 0 || is_relay_reply)
403
0
  {
404
0
    dest.sin_port = htons(daemon->dhcp_client_port); 
405
0
    dest.sin_addr = mess->ciaddr;
406
0
  }
407
0
    } 
408
0
#if defined(HAVE_LINUX_NETWORK)
409
0
  else
410
0
    {
411
      /* fill cmsg for outbound interface (both broadcast & unicast) */
412
0
      struct in_pktinfo *pkt;
413
0
      msg.msg_control = control_u.control;
414
0
      msg.msg_controllen = sizeof(control_u);
415
416
      /* alignment padding passed to the kernel should not be uninitialised. */
417
0
      memset(&control_u, 0, sizeof(control_u));
418
419
0
      cmptr = CMSG_FIRSTHDR(&msg);
420
0
      pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
421
0
      pkt->ipi_ifindex = rcvd_iface_index;
422
0
      pkt->ipi_spec_dst.s_addr = 0;
423
0
      msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
424
0
      cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
425
0
      cmptr->cmsg_level = IPPROTO_IP;
426
0
      cmptr->cmsg_type = IP_PKTINFO;
427
428
0
      if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
429
0
         mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
430
0
        {
431
          /* broadcast to 255.255.255.255 (or mac address invalid) */
432
0
          dest.sin_addr.s_addr = INADDR_BROADCAST;
433
0
          dest.sin_port = htons(daemon->dhcp_client_port);
434
0
        }
435
0
      else
436
0
        {
437
          /* unicast to unconfigured client. Inject mac address direct into ARP cache.
438
          struct sockaddr limits size to 14 bytes. */
439
0
          dest.sin_addr = mess->yiaddr;
440
0
          dest.sin_port = htons(daemon->dhcp_client_port);
441
0
          memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
442
0
          arp_req.arp_ha.sa_family = mess->htype;
443
0
          memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
444
          /* interface name already copied in */
445
0
          arp_req.arp_flags = ATF_COM;
446
0
          if (ioctl(daemon->dhcpfd, SIOCSARP, &arp_req) == -1)
447
0
            my_syslog(MS_DHCP | LOG_ERR, _("ARP-cache injection failed: %s"), strerror(errno));
448
0
        }
449
0
    }
450
#elif defined(HAVE_SOLARIS_NETWORK)
451
  else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
452
    {
453
      /* broadcast to 255.255.255.255 (or mac address invalid) */
454
      dest.sin_addr.s_addr = INADDR_BROADCAST;
455
      dest.sin_port = htons(daemon->dhcp_client_port);
456
      /* note that we don't specify the interface here: that's done by the
457
   IP_BOUND_IF sockopt lower down. */
458
    }
459
  else
460
    {
461
      /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
462
   Note that this only works for ethernet on solaris, because we use SIOCSARP
463
   and not SIOCSXARP, which would be perfect, except that it returns ENXIO 
464
   mysteriously. Bah. Fall back to broadcast for other net types. */
465
      struct arpreq req;
466
      dest.sin_addr = mess->yiaddr;
467
      dest.sin_port = htons(daemon->dhcp_client_port);
468
      *((struct sockaddr_in *)&req.arp_pa) = dest;
469
      req.arp_ha.sa_family = AF_UNSPEC;
470
      memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
471
      req.arp_flags = ATF_COM;
472
      ioctl(daemon->dhcpfd, SIOCSARP, &req);
473
    }
474
#elif defined(HAVE_BSD_NETWORK)
475
  else 
476
    {
477
#ifdef HAVE_DUMPFILE
478
      if (ntohs(mess->flags) & 0x8000)
479
        dest.sin_addr.s_addr = INADDR_BROADCAST;
480
      else
481
        dest.sin_addr = mess->yiaddr;
482
      dest.sin_port = htons(daemon->dhcp_client_port);
483
      
484
      dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
485
          (union mysockaddr *)&dest, fd);
486
#endif
487
      
488
      send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
489
      return;
490
    }
491
#endif
492
   
493
#ifdef HAVE_SOLARIS_NETWORK
494
  setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
495
#endif
496
497
0
#ifdef HAVE_DUMPFILE
498
0
  dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
499
0
      (union mysockaddr *)&dest, fd);
500
0
#endif
501
  
502
0
  while(retry_send(sendmsg(fd, &msg, 0)));
503
504
  /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
505
0
  if (errno != 0)
506
0
    {
507
0
      inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
508
0
      my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
509
0
    daemon->addrbuff, strerror(errno));
510
0
    }
511
0
}
512
513
/* check against secondary interface addresses */
514
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
515
            struct in_addr netmask, struct in_addr broadcast, void *vparam)
516
0
{
517
0
  struct match_param *param = vparam;
518
0
  struct iname *tmp;
519
520
0
  (void) label;
521
522
0
  if (if_index == param->ind)
523
0
    {
524
0
      for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
525
0
  if ( tmp->addr.sa.sa_family == AF_INET &&
526
0
       tmp->addr.in.sin_addr.s_addr == local.s_addr)
527
0
    {
528
0
      param->matched = 1;
529
0
      param->addr = local;
530
0
      param->netmask = netmask;
531
0
      param->broadcast = broadcast;
532
0
      break;
533
0
    }
534
0
    }
535
  
536
0
  return 1;
537
0
}
538
539
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple 
540
   of each interface (and any relay address) and does the  following things:
541
542
   1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
543
   2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
544
   3) Fills in local (this host) and router (this host or relay) addresses.
545
   4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
546
547
   Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
548
549
static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
550
0
{
551
0
  struct dhcp_context *context;
552
553
0
  for (context = daemon->dhcp; context; context = context->next)
554
0
    if (!(context->flags & CONTEXT_NETMASK) &&
555
0
  (is_same_net(addr, context->start, netmask) ||
556
0
   is_same_net(addr, context->end, netmask)))
557
0
      { 
558
0
  if (context->netmask.s_addr != netmask.s_addr &&
559
0
      !(is_same_net(addr, context->start, netmask) &&
560
0
        is_same_net(addr, context->end, netmask)))
561
0
    {
562
0
      inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
563
0
      inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
564
0
      inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
565
0
      my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
566
0
          daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
567
0
    } 
568
0
  context->netmask = netmask;
569
0
      }
570
0
}
571
572
static int complete_context(struct in_addr local, int if_index, char *label,
573
          struct in_addr netmask, struct in_addr broadcast, void *vparam)
574
0
{
575
0
  struct dhcp_context *context;
576
0
  struct dhcp_relay *relay;
577
0
  struct iface_param *param = vparam;
578
0
  struct shared_network *share;
579
  
580
0
  (void)label;
581
582
0
  for (share = daemon->shared_networks; share; share = share->next)
583
0
    {
584
      
585
0
#ifdef HAVE_DHCP6
586
0
      if (share->shared_addr.s_addr == 0)
587
0
  continue;
588
0
#endif
589
      
590
0
      if (share->if_index != 0)
591
0
  {
592
0
    if (share->if_index != if_index)
593
0
      continue;
594
0
  }
595
0
      else
596
0
  {
597
0
    if (share->match_addr.s_addr != local.s_addr)
598
0
      continue;
599
0
  }
600
601
0
      for (context = daemon->dhcp; context; context = context->next)
602
0
  {
603
0
    if (context->netmask.s_addr != 0 &&
604
0
        is_same_net(share->shared_addr, context->start, context->netmask) &&
605
0
        is_same_net(share->shared_addr, context->end, context->netmask))
606
0
      {
607
        /* link it onto the current chain if we've not seen it before */
608
0
        if (context->current == context)
609
0
    {
610
      /* For a shared network, we have no way to guess what the default route should be. */
611
0
      context->router.s_addr = 0;
612
0
      context->local = local; /* Use configured address for Server Identifier */
613
0
      context->current = param->current;
614
0
      param->current = context;
615
0
    }
616
        
617
0
        if (!(context->flags & CONTEXT_BRDCAST))
618
0
    context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
619
0
      }   
620
0
  }
621
0
    }
622
623
0
  guess_range_netmask(local, netmask);
624
  
625
0
  for (context = daemon->dhcp; context; context = context->next)
626
0
    {
627
0
      if (context->netmask.s_addr != 0 &&
628
0
    is_same_net(local, context->start, context->netmask) &&
629
0
    is_same_net(local, context->end, context->netmask))
630
0
  {
631
    /* link it onto the current chain if we've not seen it before */
632
0
    if (if_index == param->ind && context->current == context)
633
0
      {
634
0
        context->router = local;
635
0
        context->local = local;
636
0
        context->current = param->current;
637
0
        param->current = context;
638
0
      }
639
    
640
0
    if (!(context->flags & CONTEXT_BRDCAST))
641
0
      {
642
0
        if (is_same_net(broadcast, context->start, context->netmask))
643
0
    context->broadcast = broadcast;
644
0
        else 
645
0
    context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
646
0
      }
647
0
  }   
648
0
    }
649
650
0
  for (relay = daemon->relay4; relay; relay = relay->next)
651
0
    if (relay->local.addr4.s_addr == local.s_addr)
652
0
      relay->iface_index = if_index;
653
  
654
0
  return 1;
655
0
}
656
    
657
struct dhcp_context *address_available(struct dhcp_context *context, 
658
               struct in_addr taddr,
659
               struct dhcp_netid *netids)
660
0
{
661
  /* Check is an address is OK for this network, check all
662
     possible ranges. Make sure that the address isn't in use
663
     by the server itself. */
664
  
665
0
  unsigned int start, end, addr = ntohl(taddr.s_addr);
666
0
  struct dhcp_context *tmp;
667
668
0
  for (tmp = context; tmp; tmp = tmp->current)
669
0
    if (taddr.s_addr == context->router.s_addr)
670
0
      return NULL;
671
  
672
0
  for (tmp = context; tmp; tmp = tmp->current)
673
0
    {
674
0
      start = ntohl(tmp->start.s_addr);
675
0
      end = ntohl(tmp->end.s_addr);
676
677
0
      if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
678
0
    addr >= start &&
679
0
    addr <= end &&
680
0
    match_netid(tmp->filter, netids, 1))
681
0
  return tmp;
682
0
    }
683
684
0
  return NULL;
685
0
}
686
687
struct dhcp_context *narrow_context(struct dhcp_context *context, 
688
            struct in_addr taddr,
689
            struct dhcp_netid *netids)
690
0
{
691
  /* We start of with a set of possible contexts, all on the current physical interface.
692
     These are chained on ->current.
693
     Here we have an address, and return the actual context corresponding to that
694
     address. Note that none may fit, if the address came a dhcp-host and is outside
695
     any dhcp-range. In that case we return a static range if possible, or failing that,
696
     any context on the correct subnet. (If there's more than one, this is a dodgy 
697
     configuration: maybe there should be a warning.) */
698
  
699
0
  struct dhcp_context *tmp;
700
701
0
  if (!(tmp = address_available(context, taddr, netids)))
702
0
    {
703
0
      for (tmp = context; tmp; tmp = tmp->current)
704
0
  if (match_netid(tmp->filter, netids, 1) &&
705
0
      is_same_net(taddr, tmp->start, tmp->netmask) && 
706
0
      (tmp->flags & CONTEXT_STATIC))
707
0
    break;
708
      
709
0
      if (!tmp)
710
0
  for (tmp = context; tmp; tmp = tmp->current)
711
0
    if (match_netid(tmp->filter, netids, 1) &&
712
0
        is_same_net(taddr, tmp->start, tmp->netmask) &&
713
0
        !(tmp->flags & CONTEXT_PROXY))
714
0
      break;
715
0
    }
716
  
717
  /* Only one context allowed now */
718
0
  if (tmp)
719
0
    tmp->current = NULL;
720
  
721
0
  return tmp;
722
0
}
723
724
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
725
0
{
726
0
  struct dhcp_config *config;
727
  
728
0
  for (config = configs; config; config = config->next)
729
0
    if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
730
0
      return config;
731
732
0
  return NULL;
733
0
}
734
735
/* Check if and address is in use by sending ICMP ping.
736
   This wrapper handles a cache and load-limiting.
737
   Return is NULL is address in use, or a pointer to a cache entry
738
   recording that it isn't. */
739
struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
740
0
{
741
0
  static struct ping_result dummy;
742
0
  struct ping_result *r, *victim = NULL;
743
0
  int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
744
0
        ((float)PING_WAIT)));
745
746
  /* check if we failed to ping addr sometime in the last
747
     PING_CACHE_TIME seconds. If so, assume the same situation still exists.
748
     This avoids problems when a stupid client bangs
749
     on us repeatedly. As a final check, if we did more
750
     than 60% of the possible ping checks in the last 
751
     PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
752
0
  for (count = 0, r = daemon->ping_results; r; r = r->next)
753
0
    if (difftime(now, r->time) >  (float)PING_CACHE_TIME)
754
0
      victim = r; /* old record */
755
0
    else 
756
0
      {
757
0
  count++;
758
0
  if (r->addr.s_addr == addr.s_addr)
759
0
    return r;
760
0
      }
761
  
762
  /* didn't find cached entry */
763
0
  if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
764
0
    {
765
      /* overloaded, or configured not to check, loopback interface, return "not in use" */
766
0
      dummy.hash = hash;
767
0
      return &dummy;
768
0
    }
769
0
  else if (icmp_ping(addr))
770
0
    return NULL; /* address in use. */
771
0
  else
772
0
    {
773
      /* at this point victim may hold an expired record */
774
0
      if (!victim)
775
0
  {
776
0
    if ((victim = whine_malloc(sizeof(struct ping_result))))
777
0
      {
778
0
        victim->next = daemon->ping_results;
779
0
        daemon->ping_results = victim;
780
0
      }
781
0
  }
782
      
783
      /* record that this address is OK for 30s 
784
   without more ping checks */
785
0
      if (victim)
786
0
  {
787
0
    victim->addr = addr;
788
0
    victim->time = now;
789
0
    victim->hash = hash;
790
0
  }
791
0
      return victim;
792
0
    }
793
0
}
794
795
int address_allocate(struct dhcp_context *context,
796
         struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
797
         struct dhcp_netid *netids, time_t now, int loopback)   
798
0
{
799
  /* Find a free address: exclude anything in use and anything allocated to
800
     a particular hwaddr/clientid/hostname in our configuration.
801
     Try to return from contexts which match netids first. */
802
803
0
  struct in_addr start, addr;
804
0
  struct dhcp_context *c, *d;
805
0
  int i, pass;
806
0
  unsigned int j; 
807
808
  /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
809
     dispersal even with similarly-valued "strings". */ 
810
0
  for (j = 0, i = 0; i < hw_len; i++)
811
0
    j = hwaddr[i] + (j << 6) + (j << 16) - j;
812
813
  /* j == 0 is marker */
814
0
  if (j == 0)
815
0
    j = 1;
816
  
817
0
  for (pass = 0; pass <= 1; pass++)
818
0
    for (c = context; c; c = c->current)
819
0
      if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
820
0
  continue;
821
0
      else if (!match_netid(c->filter, netids, pass))
822
0
  continue;
823
0
      else
824
0
  {
825
0
    if (option_bool(OPT_CONSEC_ADDR))
826
      /* seed is largest extant lease addr in this context */
827
0
      start = lease_find_max_addr(c);
828
0
    else
829
      /* pick a seed based on hwaddr */
830
0
      start.s_addr = htonl(ntohl(c->start.s_addr) + 
831
0
         ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
832
833
    /* iterate until we find a free address. */
834
0
    addr = start;
835
    
836
0
    do {
837
      /* eliminate addresses in use by the server. */
838
0
      for (d = context; d; d = d->current)
839
0
        if (addr.s_addr == d->router.s_addr)
840
0
    break;
841
842
      /* Addresses which end in .255 and .0 are broken in Windows even when using 
843
         supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
844
         then 192.168.0.255 is a valid IP address, but not for Windows as it's
845
         in the class C range. See  KB281579. We therefore don't allocate these 
846
         addresses to avoid hard-to-diagnose problems. Thanks Bill. */      
847
0
      if (!d &&
848
0
    !lease_find_by_addr(addr) && 
849
0
    !config_find_by_address(daemon->dhcp_conf, addr) &&
850
0
    (!IN_CLASSC(ntohl(addr.s_addr)) || 
851
0
     ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
852
0
        {
853
    /* in consec-ip mode, skip addresses equal to
854
       the number of addresses rejected by clients. This
855
       should avoid the same client being offered the same
856
       address after it has rjected it. */
857
0
    if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
858
0
      c->addr_epoch--;
859
0
    else
860
0
      {
861
0
        struct ping_result *r;
862
        
863
0
        if ((r = do_icmp_ping(now, addr, j, loopback)))
864
0
          {
865
      /* consec-ip mode: we offered this address for another client
866
         (different hash) recently, don't offer it to this one. */
867
0
      if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
868
0
        {
869
0
          *addrp = addr;
870
0
          return 1;
871
0
        }
872
0
          }
873
0
        else
874
0
          {
875
      /* address in use: perturb address selection so that we are
876
         less likely to try this address again. */
877
0
      if (!option_bool(OPT_CONSEC_ADDR))
878
0
        c->addr_epoch++;
879
0
          }
880
0
      }
881
0
        }
882
      
883
0
      addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
884
      
885
0
      if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
886
0
        addr = c->start;
887
      
888
0
    } while (addr.s_addr != start.s_addr);
889
0
  }
890
891
0
  return 0;
892
0
}
893
894
void dhcp_read_ethers(void)
895
0
{
896
0
  FILE *f = fopen(ETHERSFILE, "r");
897
0
  unsigned int flags;
898
0
  char *buff = daemon->namebuff;
899
0
  char *ip, *cp;
900
0
  struct in_addr addr;
901
0
  unsigned char hwaddr[ETHER_ADDR_LEN];
902
0
  struct dhcp_config **up, *tmp;
903
0
  struct dhcp_config *config;
904
0
  int count = 0, lineno = 0;
905
906
0
  addr.s_addr = 0; /* eliminate warning */
907
  
908
0
  if (!f)
909
0
    {
910
0
      my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
911
0
      return;
912
0
    }
913
914
  /* This can be called again on SIGHUP, so remove entries created last time round. */
915
0
  for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
916
0
    {
917
0
      tmp = config->next;
918
0
      if (config->flags & CONFIG_FROM_ETHERS)
919
0
  {
920
0
    *up = tmp;
921
    /* cannot have a clid */
922
0
    if (config->flags & CONFIG_NAME)
923
0
      free(config->hostname);
924
0
    free(config->hwaddr);
925
0
    free(config);
926
0
  }
927
0
      else
928
0
  up = &config->next;
929
0
    }
930
931
0
  while (fgets(buff, MAXDNAME, f))
932
0
    {
933
0
      char *host = NULL;
934
      
935
0
      lineno++;
936
      
937
0
      while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
938
0
  buff[strlen(buff)-1] = 0;
939
      
940
0
      if ((*buff == '#') || (*buff == '+') || (*buff == 0))
941
0
  continue;
942
      
943
0
      for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
944
0
      for(; *ip && isspace((unsigned char)*ip); ip++)
945
0
  *ip = 0;
946
0
      if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
947
0
  {
948
0
    my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); 
949
0
    continue;
950
0
  }
951
      
952
      /* check for name or dotted-quad */
953
0
      for (cp = ip; *cp; cp++)
954
0
  if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
955
0
    break;
956
      
957
0
      if (!*cp)
958
0
  {
959
0
    if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
960
0
      {
961
0
        my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); 
962
0
        continue;
963
0
      }
964
965
0
    flags = CONFIG_ADDR;
966
    
967
0
    for (config = daemon->dhcp_conf; config; config = config->next)
968
0
      if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
969
0
        break;
970
0
  }
971
0
      else 
972
0
  {
973
0
    int nomem;
974
0
    if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
975
0
      {
976
0
        if (!nomem)
977
0
    my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); 
978
0
        free(host);
979
0
        continue;
980
0
      }
981
        
982
0
    flags = CONFIG_NAME;
983
984
0
    for (config = daemon->dhcp_conf; config; config = config->next)
985
0
      if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
986
0
        break;
987
0
  }
988
989
0
      if (config && (config->flags & CONFIG_FROM_ETHERS))
990
0
  {
991
0
    my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno); 
992
0
    continue;
993
0
  }
994
  
995
0
      if (!config)
996
0
  { 
997
0
    for (config = daemon->dhcp_conf; config; config = config->next)
998
0
      {
999
0
        struct hwaddr_config *conf_addr = config->hwaddr;
1000
0
        if (conf_addr && 
1001
0
      conf_addr->next == NULL && 
1002
0
      conf_addr->wildcard_mask == 0 &&
1003
0
      conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
1004
0
      (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
1005
0
      memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
1006
0
    break;
1007
0
      }
1008
    
1009
0
    if (!config)
1010
0
      {
1011
0
        if (!(config = whine_malloc(sizeof(struct dhcp_config))))
1012
0
    continue;
1013
0
        config->flags = CONFIG_FROM_ETHERS;
1014
0
        config->hwaddr = NULL;
1015
0
        config->domain = NULL;
1016
0
        config->netid = NULL;
1017
0
        config->next = daemon->dhcp_conf;
1018
0
        daemon->dhcp_conf = config;
1019
0
      }
1020
    
1021
0
    config->flags |= flags;
1022
    
1023
0
    if (flags & CONFIG_NAME)
1024
0
      {
1025
0
        config->hostname = host;
1026
0
        host = NULL;
1027
0
      }
1028
    
1029
0
    if (flags & CONFIG_ADDR)
1030
0
      config->addr = addr;
1031
0
  }
1032
      
1033
0
      config->flags |= CONFIG_NOCLID;
1034
0
      if (!config->hwaddr)
1035
0
  config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
1036
0
      if (config->hwaddr)
1037
0
  {
1038
0
    memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
1039
0
    config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
1040
0
    config->hwaddr->hwaddr_type = ARPHRD_ETHER;
1041
0
    config->hwaddr->wildcard_mask = 0;
1042
0
    config->hwaddr->next = NULL;
1043
0
  }
1044
0
      count++;
1045
      
1046
0
      free(host);
1047
1048
0
    }
1049
  
1050
0
  fclose(f);
1051
1052
0
  my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
1053
0
}
1054
1055
1056
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
1057
   for this address. If it has a domain part, that must match the set domain and
1058
   it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
1059
   so check here that the domain name is legal as a hostname. 
1060
   NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
1061
char *host_from_dns(struct in_addr addr)
1062
0
{
1063
0
  struct crec *lookup;
1064
1065
0
  if (daemon->port == 0)
1066
0
    return NULL; /* DNS disabled. */
1067
  
1068
0
  lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
1069
1070
0
  if (lookup && (lookup->flags & F_HOSTS))
1071
0
    {
1072
0
      char *dot, *hostname = cache_get_name(lookup);
1073
0
      dot = strchr(hostname, '.');
1074
      
1075
0
      if (dot && strlen(dot+1) != 0)
1076
0
  {
1077
0
    char *d2 = get_domain(addr);
1078
0
    if (!d2 || !hostname_isequal(dot+1, d2))
1079
0
      return NULL; /* wrong domain */
1080
0
  }
1081
1082
0
      if (!legal_hostname(hostname))
1083
0
  return NULL;
1084
      
1085
0
      safe_strncpy(daemon->dhcp_buff, hostname, 256);
1086
0
      strip_hostname(daemon->dhcp_buff);
1087
1088
0
      return daemon->dhcp_buff;
1089
0
    }
1090
  
1091
0
  return NULL;
1092
0
}
1093
1094
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
1095
0
{
1096
0
  struct in_addr giaddr = mess->giaddr;
1097
0
  u8 hops = mess->hops;
1098
0
  struct dhcp_relay *relay;
1099
1100
0
  if (mess->op != BOOTREQUEST)
1101
0
    return;
1102
1103
0
  for (relay = daemon->relay4; relay; relay = relay->next)
1104
0
    if (relay->iface_index != 0 && relay->iface_index == iface_index)
1105
0
      break;
1106
1107
  /* No relay config. */
1108
0
  if (!relay)
1109
0
    return;
1110
  
1111
0
  for (; relay; relay = relay->next)
1112
0
    if (relay->iface_index != 0 && relay->iface_index == iface_index)
1113
0
      {
1114
0
  union mysockaddr to;
1115
0
  union all_addr from;
1116
1117
0
  mess->hops = hops;
1118
0
  mess->giaddr = giaddr;
1119
  
1120
0
  if ((mess->hops++) > 20)
1121
0
    continue;
1122
  
1123
  /* source address == relay address */
1124
0
  from.addr4 = relay->local.addr4;
1125
1126
  /* already gatewayed ? */
1127
0
  if (giaddr.s_addr)
1128
0
    {
1129
      /* if so check if by us, to stomp on loops. */
1130
0
      if (giaddr.s_addr == relay->local.addr4.s_addr)
1131
0
        continue;
1132
0
    }
1133
0
  else
1134
0
    {
1135
      /* plug in our address */
1136
0
      mess->giaddr.s_addr = relay->local.addr4.s_addr;
1137
0
    }
1138
  
1139
0
  to.sa.sa_family = AF_INET;
1140
0
  to.in.sin_addr = relay->server.addr4;
1141
0
  to.in.sin_port = htons(relay->port);
1142
#ifdef HAVE_SOCKADDR_SA_LEN
1143
  to.in.sin_len = sizeof(struct sockaddr_in);
1144
#endif
1145
  
1146
  /* Broadcasting to server. */
1147
0
  if (relay->server.addr4.s_addr == 0)
1148
0
    {
1149
0
      struct ifreq ifr;
1150
      
1151
0
      if (relay->interface)
1152
0
        safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
1153
      
1154
0
      if (!relay->interface || strchr(relay->interface, '*') ||
1155
0
    ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
1156
0
        {
1157
0
    my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
1158
0
    continue;
1159
0
        }
1160
      
1161
0
      to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
1162
0
    }
1163
  
1164
0
#ifdef HAVE_DUMPFILE
1165
0
  {
1166
0
    union mysockaddr fromsock;
1167
0
    fromsock.in.sin_port = htons(daemon->dhcp_server_port);
1168
0
    fromsock.in.sin_addr = from.addr4;
1169
0
    fromsock.sa.sa_family = AF_INET;
1170
1171
0
    dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
1172
0
  }
1173
0
#endif
1174
  
1175
0
   send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
1176
   
1177
0
   if (option_bool(OPT_LOG_OPTS))
1178
0
     {
1179
0
       inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
1180
0
       if (relay->server.addr4.s_addr == 0)
1181
0
         snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
1182
0
       else
1183
0
         inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
1184
0
       my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
1185
0
     }
1186
0
      }
1187
  
1188
  /* restore in case of a local reply. */
1189
0
  mess->giaddr = giaddr;
1190
0
}
1191
1192
1193
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
1194
0
{
1195
0
  struct dhcp_relay *relay;
1196
1197
0
  if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
1198
0
    return NULL;
1199
1200
0
  for (relay = daemon->relay4; relay; relay = relay->next)
1201
0
    {
1202
0
      if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
1203
0
  {
1204
0
    if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
1205
0
      return relay->iface_index != 0 ? relay : NULL;
1206
0
  }
1207
0
    }
1208
  
1209
0
  return NULL;  
1210
0
}     
1211
1212
#endif