blob: 58091262d06665fdd2387b0065b9c405090f399d [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 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.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 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/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010022#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000023
Simon Kelley316e2732010-01-22 20:16:09 +000024#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +000025static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
26#endif
Simon Kelley572b41e2011-02-18 18:11:18 +000027
Simon Kelleyf2621c72007-04-29 19:47:21 +010028static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000029static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000030static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000031static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
32static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
33 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034static struct in_addr option_addr(unsigned char *opt);
Rosen Penev50a28412017-06-27 22:27:02 +010035static unsigned int option_uint(unsigned char *opt, int offset, int size);
Simon Kelley7622fc02009-06-04 20:32:05 +010036static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +010037 int mac_len, char *interface, char *string, char *err, u32 xid);
Simon Kelleycdeda282006-03-16 20:16:06 +000038static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010039static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley7de060b2011-08-26 17:24:52 +010040static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
Simon Kelleya9df0e32017-04-28 22:43:00 +010041static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
Simon Kelleyaa63a212013-04-22 15:01:52 +010042static int in_list(unsigned char *list, int opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000043static void do_options(struct dhcp_context *context,
44 struct dhcp_packet *mess,
Rosen Penev50a28412017-06-27 22:27:02 +010045 unsigned char *end,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000046 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000047 char *hostname,
Rosen Penev50a28412017-06-27 22:27:02 +010048 char *domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000049 struct dhcp_netid *netid,
Simon Kelley7de060b2011-08-26 17:24:52 +010050 struct in_addr subnet_addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000051 unsigned char fqdn_flags,
Rosen Penev50a28412017-06-27 22:27:02 +010052 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +010053 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +010054 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +010055 time_t now,
56 unsigned int lease_time,
57 unsigned short fuzz);
Simon Kelley7622fc02009-06-04 20:32:05 +010058
Simon Kelley9009d742008-11-14 20:04:27 +000059
Simon Kelley6b010842007-02-12 20:32:07 +000060static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Rosen Penev50a28412017-06-27 22:27:02 +010061static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
Simon Kelley7622fc02009-06-04 20:32:05 +010062static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
63static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000064static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010065struct dhcp_boot *find_boot(struct dhcp_netid *netid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +010066static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
Floris Bos503c6092017-04-09 23:07:13 +010067static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
68
Simon Kelley824af852008-02-12 20:43:05 +000069size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelleyc7be0162017-05-10 22:21:53 +010070 size_t sz, time_t now, int unicast_dest, int loopback,
71 int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
Simon Kelley33820b72004-04-03 21:10:00 +010072{
Simon Kelley26128d22004-11-14 16:43:54 +000073 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000074 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010075 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000076 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000077 struct dhcp_netid_list *id_list;
Simon Kelley734d5312018-03-23 23:09:53 +000078 int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000079 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000080 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010081 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000082 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000083 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010084 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000085 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000086 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000087 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010088 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010089 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010090 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010091 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010092 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010093 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000094 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010095 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000096 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000097 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010098 unsigned char pxe_uuid[17];
Vladislav Grishenko99e88912013-11-26 11:02:29 +000099 unsigned char *oui = NULL, *serial = NULL;
100#ifdef HAVE_SCRIPT
101 unsigned char *class = NULL;
102#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100103
Simon Kelley1a6bca82008-07-11 11:11:42 +0100104 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000105
106 /* set tag with name == interface */
107 iface_id.net = iface_name;
108 iface_id.next = NULL;
109 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100110
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100112 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000113
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100114 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000115 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100116
Simon Kelley3be34542004-09-11 19:12:13 +0100117 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000118 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000119 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100120 u32 cookie = htonl(DHCP_COOKIE);
121
Simon Kelley3be34542004-09-11 19:12:13 +0100122 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100123 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100124 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100125
126 mess_type = option_uint(opt, 0, 1);
127
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100128 /* two things to note here: expand_buf may move the packet,
129 so reassign mess from daemon->packet. Also, the size
130 sent includes the IP and UDP headers, hence the magic "-28" */
131 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
132 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100133 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100134
135 if (size > DHCP_PACKET_MAX)
136 size = DHCP_PACKET_MAX;
137 else if (size < sizeof(struct dhcp_packet))
138 size = sizeof(struct dhcp_packet);
139
140 if (expand_buf(&daemon->dhcp_packet, size))
141 {
Simon Kelley824af852008-02-12 20:43:05 +0000142 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100143 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 }
145 }
146
Simon Kelley3be34542004-09-11 19:12:13 +0100147 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
148 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000149 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100150 mess->ciaddr.s_addr = 0;
151
Simon Kelley316e2732010-01-22 20:16:09 +0000152 /* search for device identity from CPEWAN devices, we pass this through to the script */
153 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
154 {
155 unsigned int elen, offset, len = option_len(opt);
156
157 for (offset = 0; offset < (len - 5); offset += elen + 5)
158 {
159 elen = option_uint(opt, offset + 4 , 1);
Simon Kelley6a0b00f2017-09-25 20:19:55 +0100160 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
Simon Kelley316e2732010-01-22 20:16:09 +0000161 {
162 unsigned char *x = option_ptr(opt, offset + 5);
163 unsigned char *y = option_ptr(opt, offset + elen + 5);
164 oui = option_find1(x, y, 1, 1);
165 serial = option_find1(x, y, 2, 1);
Vladislav Grishenko99e88912013-11-26 11:02:29 +0000166#ifdef HAVE_SCRIPT
167 class = option_find1(x, y, 3, 1);
168#endif
Simon Kelley316e2732010-01-22 20:16:09 +0000169 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
170 the gateway id back. Note that the device class is optional */
171 if (oui && serial)
172 {
173 cpewan_id.net = "cpewan-id";
174 cpewan_id.next = netid;
175 netid = &cpewan_id;
176 }
177 break;
178 }
179 }
180 }
181
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100182 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
183 {
184 /* Any agent-id needs to be copied back out, verbatim, as the last option
185 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
186 get overwritten, then it will be shuffled back at the end of processing.
187 Note that the incoming options must not be overwritten here, so there has to
188 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100189 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100190 unsigned int total = option_len(opt) + 2;
Simon Kelley591ed1e2016-07-11 18:18:42 +0100191 unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz,
192 OPTION_END, 0);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100193 if (last_opt && last_opt < end - total)
194 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100195 end -= total;
196 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100197 memcpy(agent_id, opt, total);
198 }
199
200 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100201 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100202 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100203
204 /* look for RFC5107 server-identifier-override */
205 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
206 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100207
208 /* if a circuit-id or remote-is option is provided, exact-match to options. */
209 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
210 {
211 int search;
212
213 if (vendor->match_type == MATCH_CIRCUIT)
214 search = SUBOPT_CIRCUIT_ID;
215 else if (vendor->match_type == MATCH_REMOTE)
216 search = SUBOPT_REMOTE_ID;
217 else if (vendor->match_type == MATCH_SUBSCRIBER)
218 search = SUBOPT_SUBSCR_ID;
219 else
220 continue;
221
Simon Kelley1a6bca82008-07-11 11:11:42 +0100222 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100223 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100224 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100225 {
226 vendor->netid.next = netid;
227 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100228 }
229 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100230 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100231
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100232 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
233 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100234 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000235
236 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000237 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000238 {
Simon Kelley26128d22004-11-14 16:43:54 +0000239 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100240 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000241 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000242
Simon Kelley0a852542005-03-23 20:28:59 +0000243 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100244 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000245
246 /* If this request is missing a clid, but we've seen one before,
247 use it again for option matching etc. */
248 if (lease && !clid && lease->clid)
249 {
250 clid_len = lease->clid_len;
251 clid = lease->clid;
252 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000253
254 /* find mac to use for logging and hashing */
255 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000256 }
Simon Kelley3be34542004-09-11 19:12:13 +0100257
Simon Kelleycdeda282006-03-16 20:16:06 +0000258 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
259 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100260 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
261 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000262 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263 mac->netid.next = netid;
264 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000265 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100266
Simon Kelley0a852542005-03-23 20:28:59 +0000267 /* Determine network for this packet. Our caller will have already linked all the
268 contexts which match the addresses of the receiving interface but if the
269 machine has an address already, or came via a relay, or we have a subnet selector,
270 we search again. If we don't have have a giaddr or explicit subnet selector,
271 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100272 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
273 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000274 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
275 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100276 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100277 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100278 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000279
Simon Kelley3d8df262005-08-29 12:19:27 +0100280 if (subnet_addr.s_addr)
281 {
282 addr = subnet_addr;
283 force = 1;
284 }
285 else if (mess->giaddr.s_addr)
286 {
287 addr = mess->giaddr;
288 force = 1;
289 }
Simon Kelley16972692006-10-16 20:04:18 +0100290 else
291 {
292 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
293 addr = mess->ciaddr;
294 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
295 if (context_tmp->netmask.s_addr &&
296 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
297 is_same_net(addr, context_tmp->end, context_tmp->netmask))
298 {
299 context_new = context;
300 break;
301 }
302 }
303
304 if (!context_new)
305 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100306 {
307 struct in_addr netmask = context_tmp->netmask;
308
309 /* guess the netmask for relayed networks */
310 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
311 {
312 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
313 netmask.s_addr = htonl(0xff000000);
314 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
315 netmask.s_addr = htonl(0xffff0000);
316 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
317 netmask.s_addr = htonl(0xffffff00);
318 }
319
320 /* This section fills in context mainly when a client which is on a remote (relayed)
321 network renews a lease without using the relay, after dnsmasq has restarted. */
322 if (netmask.s_addr != 0 &&
323 is_same_net(addr, context_tmp->start, netmask) &&
324 is_same_net(addr, context_tmp->end, netmask))
325 {
326 context_tmp->netmask = netmask;
327 if (context_tmp->local.s_addr == 0)
328 context_tmp->local = fallback;
329 if (context_tmp->router.s_addr == 0)
330 context_tmp->router = mess->giaddr;
331
332 /* fill in missing broadcast addresses for relayed ranges */
333 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
334 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
335
336 context_tmp->current = context_new;
337 context_new = context_tmp;
338 }
339 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100340
Simon Kelley3d8df262005-08-29 12:19:27 +0100341 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100342 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000343 }
Simon Kelley3be34542004-09-11 19:12:13 +0100344
345 if (!context)
346 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100347 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100348 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
349 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
Simon Kelley3be34542004-09-11 19:12:13 +0100350 return 0;
351 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100352
Simon Kelley28866e92011-02-14 20:19:14 +0000353 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100354 {
355 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100356 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
357 {
358 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100359 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100360 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100361 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100362 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100363 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100364 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100365 }
366 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100367
368 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
369 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000370 If no data given, existence of the option is enough. This code handles
Simon Kelley86e92f92013-04-23 11:31:39 +0100371 rfc3925 V-I classes too. */
372 for (o = daemon->dhcp_match; o; o = o->next)
373 {
374 unsigned int len, elen, match = 0;
375 size_t offset, o2;
376
377 if (o->flags & DHOPT_RFC3925)
378 {
379 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
380 continue;
381
382 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
383 {
384 len = option_uint(opt, offset + 4 , 1);
385 /* Need to take care that bad data can't run us off the end of the packet */
Matthias Andree9828ab12017-05-21 22:41:16 +0100386 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100387 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
388 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
389 {
390 elen = option_uint(opt, o2, 1);
391 if ((o2 + elen + 1 <= option_len(opt)) &&
392 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
393 break;
394 }
395 if (match)
396 break;
397 }
398 }
399 else
400 {
401 if (!(opt = option_find(mess, sz, o->opt, 1)))
402 continue;
403
404 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
405 }
406
407 if (match)
408 {
409 o->netid->next = netid;
410 netid = o->netid;
411 }
412 }
413
414 /* user-class options are, according to RFC3004, supposed to contain
415 a set of counted strings. Here we check that this is so (by seeing
416 if the counts are consistent with the overall option length) and if
417 so zero the counts so that we don't get spurious matches between
418 the vendor string and the counts. If the lengths don't add up, we
419 assume that the option is a single string and non RFC3004 compliant
420 and just do the substring match. dhclient provides these broken options.
421 The code, later, which sends user-class data to the lease-change script
422 relies on the transformation done here.
423 */
424
425 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
426 {
427 unsigned char *ucp = option_ptr(opt, 0);
428 int tmp, j;
429 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
430 if (j == option_len(opt))
431 for (j = 0; j < option_len(opt); j = tmp)
432 {
433 tmp = j + ucp[j] + 1;
434 ucp[j] = 0;
435 }
436 }
437
438 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
439 {
440 int mopt;
441
442 if (vendor->match_type == MATCH_VENDOR)
443 mopt = OPTION_VENDOR_ID;
444 else if (vendor->match_type == MATCH_USER)
445 mopt = OPTION_USER_CLASS;
446 else
447 continue;
448
449 if ((opt = option_find(mess, sz, mopt, 1)))
450 {
451 int i;
452 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
453 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
454 {
455 vendor->netid.next = netid;
456 netid = &vendor->netid;
457 break;
458 }
459 }
460 }
461
462 /* mark vendor-encapsulated options which match the client-supplied vendor class,
463 save client-supplied vendor class */
464 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
465 {
466 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
467 vendor_class_len = option_len(opt);
468 }
469 match_vendor_opts(opt, daemon->dhcp_opts);
470
471 if (option_bool(OPT_LOG_OPTS))
472 {
473 if (sanitise(opt, daemon->namebuff))
474 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
475 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
476 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
477 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100478
Simon Kelley3be34542004-09-11 19:12:13 +0100479 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100480
Simon Kelleycdeda282006-03-16 20:16:06 +0000481 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
482 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100483
484 /* set "known" tag for known hosts */
485 if (config)
486 {
487 known_id.net = "known";
488 known_id.next = netid;
489 netid = &known_id;
490 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100491 else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
492 mess->chaddr, mess->hlen, mess->htype, NULL))
493 {
494 known_id.net = "known-othernet";
495 known_id.next = netid;
496 netid = &known_id;
497 }
Simon Kelley26128d22004-11-14 16:43:54 +0000498
Simon Kelley316e2732010-01-22 20:16:09 +0000499 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100500 {
501 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000502 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000503 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100504
505 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100506 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100507 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000508
Simon Kelley26128d22004-11-14 16:43:54 +0000509 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000510 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000511
512 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
513
514 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000515 {
516 hostname = config->hostname;
517 domain = config->domain;
518 }
519
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100520 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000521 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100522 struct dhcp_netid_list *list;
523
524 for (list = config->netid; list; list = list->next)
525 {
526 list->list->next = netid;
527 netid = list->list;
528 }
Simon Kelley26128d22004-11-14 16:43:54 +0000529 }
530
531 /* Match incoming filename field as a netid. */
532 if (mess->file[0])
533 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000534 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
535 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
536 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000537 id.next = netid;
538 netid = &id;
539 }
Simon Kelley6b010842007-02-12 20:32:07 +0000540
541 /* Add "bootp" as a tag to allow different options, address ranges etc
542 for BOOTP clients */
543 bootp_id.net = "bootp";
544 bootp_id.next = netid;
545 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000546
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100547 tagif_netid = run_tag_if(netid);
548
Simon Kelley26128d22004-11-14 16:43:54 +0000549 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100550 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100551 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000552
Simon Kelley3d8df262005-08-29 12:19:27 +0100553 if (!message)
554 {
Simon Kelley9009d742008-11-14 20:04:27 +0000555 int nailed = 0;
556
Simon Kelley3d8df262005-08-29 12:19:27 +0100557 if (have_config(config, CONFIG_ADDR))
558 {
Simon Kelley9009d742008-11-14 20:04:27 +0000559 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100560 logaddr = &config->addr;
561 mess->yiaddr = config->addr;
562 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000563 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100564 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000565 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000566 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100567 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100568 else
569 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000570 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100571 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000572 {
573 if (lease)
574 {
575 /* lease exists, wrong network. */
576 lease_prune(lease, now);
577 lease = NULL;
578 }
Simon Kelleyc7be0162017-05-10 22:21:53 +0100579 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
Simon Kelleye17fb622006-01-14 20:33:46 +0000580 message = _("no address available");
581 }
582 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100583 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100584 }
585
Simon Kelley9009d742008-11-14 20:04:27 +0000586 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
587 message = _("wrong network");
588 else if (context->netid.net)
589 {
590 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100591 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100592 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100593
Simon Kelley4cb1b322012-02-06 14:30:41 +0000594 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100595
Simon Kelley9009d742008-11-14 20:04:27 +0000596 if (!message && !nailed)
597 {
598 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100599 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000600 break;
601 if (!id_list)
602 message = _("no address configured");
603 }
604
Simon Kelley7cebd202006-05-06 14:13:33 +0100605 if (!message &&
606 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000607 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000608 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000609
Simon Kelley3d8df262005-08-29 12:19:27 +0100610 if (!message)
611 {
612 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100613
Simon Kelleya9ab7322012-04-28 11:29:37 +0100614 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100615 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000616 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100617 /* infinite lease unless nailed in dhcp-host line. */
618 lease_set_expires(lease,
619 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
620 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000621 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100622
Simon Kelleya9df0e32017-04-28 22:43:00 +0100623 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000624 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +0100625 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100626 }
627 }
628
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100629 daemon->metrics[METRIC_BOOTP]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100630 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000631
Simon Kelley7de060b2011-08-26 17:24:52 +0100632 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100633 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000634
Roy Marples3f3adae2013-07-25 16:22:46 +0100635 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100636 {
637 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
638 int len = option_len(opt);
639 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100640 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100641
Simon Kelley9fed0f72012-08-30 11:43:35 +0100642 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100643 len -= 3;
644 op += 3;
645 pp = op;
646
Simon Kelley9fed0f72012-08-30 11:43:35 +0100647 /* NB, the following always sets at least one bit */
648 if (option_bool(OPT_FQDN_UPDATE))
649 {
650 if (fqdn_flags & 0x01)
651 {
652 fqdn_flags |= 0x02; /* set O */
653 fqdn_flags &= ~0x01; /* clear S */
654 }
655 fqdn_flags |= 0x08; /* set N */
656 }
657 else
658 {
659 if (!(fqdn_flags & 0x01))
660 fqdn_flags |= 0x03; /* set S and O */
661 fqdn_flags &= ~0x08; /* clear N */
662 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100663
664 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100665 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100666 {
667 memcpy(pq, op+1, *op);
668 pq += *op;
669 op += (*op)+1;
670 *(pq++) = '.';
671 }
672 else
673 {
674 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000675 if (len > 0 && op[len-1] == 0)
676 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100677 pq += len + 1;
678 }
679
680 if (pq != daemon->dhcp_buff)
681 pq--;
682
683 *pq = 0;
684
Simon Kelley1f15b812009-10-13 17:49:32 +0100685 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100686 offer_hostname = client_hostname = daemon->dhcp_buff;
687 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000688 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000689 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000690 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100691 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000692 /* Microsoft clients are broken, and need zero-terminated strings
693 in options. We detect this state here, and do the same in
694 any options we send */
695 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
696 borken_opt = 1;
697 else
698 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100699 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100700 client_hostname = daemon->dhcp_buff;
701 }
702
Simon Kelleyc8226202018-08-08 23:46:03 +0100703 if (client_hostname)
704 {
705 struct dhcp_match_name *m;
706 size_t nl = strlen(client_hostname);
707
708 if (option_bool(OPT_LOG_OPTS))
709 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
710
711
712 for (m = daemon->dhcp_name_match; m; m = m->next)
713 {
714 size_t ml = strlen(m->name);
715 char save = 0;
716
717 if (nl < ml)
718 continue;
719 if (nl > ml)
720 {
721 save = client_hostname[ml];
722 client_hostname[ml] = 0;
723 }
724
725 if (hostname_isequal(client_hostname, m->name) &&
726 (save == 0 || m->wildcard))
727 {
728 m->netid->next = netid;
729 netid = m->netid;
730 }
731
732 if (save != 0)
733 client_hostname[ml] = save;
734 }
735 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100736
Simon Kelley3d8df262005-08-29 12:19:27 +0100737 if (have_config(config, CONFIG_NAME))
738 {
739 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000740 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000741 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000742 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100743 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000744 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100745 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100746 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100747 {
Simon Kelley9009d742008-11-14 20:04:27 +0000748 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100749
750 if (strlen(client_hostname) != 0)
751 {
752 hostname = client_hostname;
753 if (!config)
754 {
755 /* Search again now we have a hostname.
756 Only accept configs without CLID and HWADDR here, (they won't match)
757 to avoid impersonation by name. */
758 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
759 mess->chaddr, mess->hlen,
760 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000761 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000762 {
763 config = new;
764 /* set "known" tag for known hosts */
765 known_id.net = "known";
766 known_id.next = netid;
767 netid = &known_id;
768 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100769 }
770 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000771 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100772
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100773 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100774 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100775 struct dhcp_netid_list *list;
776
777 for (list = config->netid; list; list = list->next)
778 {
779 list->list->next = netid;
780 netid = list->list;
781 }
Simon Kelleya2226412004-05-13 20:27:08 +0100782 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100783
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100784 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100785
Simon Kelley26128d22004-11-14 16:43:54 +0000786 /* if all the netids in the ignore list are present, ignore this client */
787 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100788 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000789 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100790
791 /* If configured, we can override the server-id to be the address of the relay,
792 so that all traffic goes via the relay and can pick up agent-id info. This can be
793 configured for all relays, or by address. */
794 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
795 {
796 if (!daemon->override_relays)
797 override = mess->giaddr;
798 else
799 {
800 struct addr_list *l;
801 for (l = daemon->override_relays; l; l = l->next)
802 if (l->addr.s_addr == mess->giaddr.s_addr)
803 break;
804 if (l)
805 override = mess->giaddr;
806 }
807 }
808
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100809 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
810 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000811 clid = NULL;
812
Simon Kelley7622fc02009-06-04 20:32:05 +0100813 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100814 if (daemon->enable_pxe &&
815 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100816 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
817 {
818 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
819 {
820 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
821 uuid = pxe_uuid;
822 }
823
824 /* Check if this is really a PXE bootserver request, and handle specially if so. */
825 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
826 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
827 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
828 {
829 struct pxe_service *service;
830 int type = option_uint(opt, 0, 2);
831 int layer = option_uint(opt, 2, 2);
832 unsigned char save71[4];
833 struct dhcp_opt opt71;
834
Simon Kelley1f15b812009-10-13 17:49:32 +0100835 if (ignore)
836 return 0;
837
Simon Kelley7622fc02009-06-04 20:32:05 +0100838 if (layer & 0x8000)
839 {
840 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
841 return 0;
842 }
843
844 memcpy(save71, option_ptr(opt, 0), 4);
845
846 for (service = daemon->pxe_services; service; service = service->next)
847 if (service->type == type)
848 break;
849
Simon Kelley549b1a42015-05-20 20:20:24 +0100850 for (; context; context = context->current)
851 if (match_netid(context->filter, tagif_netid, 1) &&
852 is_same_net(mess->ciaddr, context->start, context->netmask))
853 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100854
Simon Kelley549b1a42015-05-20 20:20:24 +0100855 if (!service || !service->basename || !context)
856 return 0;
857
Simon Kelleya9df0e32017-04-28 22:43:00 +0100858 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100859
860 mess->yiaddr = mess->ciaddr;
861 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000862 if (service->sname)
863 mess->siaddr = a_record_from_hosts(service->sname, now);
864 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100865 mess->siaddr = service->server;
866 else
867 mess->siaddr = context->local;
868
Matthias Andreef77700a2017-05-21 22:36:09 +0100869 if (strchr(service->basename, '.'))
870 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100871 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100872 else
873 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100874 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100875
Simon Kelley7622fc02009-06-04 20:32:05 +0100876 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
877 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
878 pxe_misc(mess, end, uuid);
879
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100880 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100881 opt71.val = save71;
882 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
883 opt71.len = 4;
884 opt71.flags = DHOPT_VENDOR_MATCH;
885 opt71.netid = NULL;
886 opt71.next = daemon->dhcp_opts;
887 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
888
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100889 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000890 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100891 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100892 }
893
894 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
895 {
896 pxearch = option_uint(opt, 0, 2);
897
Simon Kelley316e2732010-01-22 20:16:09 +0000898 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000899 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100900 {
Simon Kelley28866e92011-02-14 20:19:14 +0000901 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100902 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100903
Simon Kelley28866e92011-02-14 20:19:14 +0000904 for (tmp = context; tmp; tmp = tmp->current)
905 if ((tmp->flags & CONTEXT_PROXY) &&
906 match_netid(tmp->filter, tagif_netid, 1))
907 break;
908
909 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100910 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100911 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100912 int redirect4011 = 0;
913
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100914 if (tmp->netid.net)
915 {
916 tmp->netid.next = netid;
917 tagif_netid = run_tag_if(&tmp->netid);
918 }
919
920 boot = find_boot(tagif_netid);
921
Simon Kelley28866e92011-02-14 20:19:14 +0000922 mess->yiaddr.s_addr = 0;
923 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
924 {
925 mess->ciaddr.s_addr = 0;
926 mess->flags |= htons(0x8000); /* broadcast */
927 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100928
Simon Kelleya9df0e32017-04-28 22:43:00 +0100929 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000930
Simon Kelley0a4a0492016-05-15 20:13:45 +0100931 /* Redirect EFI clients to port 4011 */
932 if (pxearch >= 6)
933 {
934 redirect4011 = 1;
935 mess->siaddr = tmp->local;
936 }
937
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100938 /* Returns true if only one matching service is available. On port 4011,
939 it also inserts the boot file and server name. */
940 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100941
942 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000943 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000944 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100945 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100946 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000947 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100948 else if (boot->tftp_sname)
949 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000950
951 if (boot->file)
952 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
953 }
954
955 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
956 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100957 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Simon Kelley28866e92011-02-14 20:19:14 +0000958 pxe_misc(mess, end, uuid);
959 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +0100960 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +0100961 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
962
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100963 daemon->metrics[METRIC_PXE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100964 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000965 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +0100966 if (!ignore)
967 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100968 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100969 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100970 }
971 }
972 }
973
974 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000975 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100976 return 0;
977
Simon Kelleybb01cb92004-12-13 20:56:23 +0000978 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100979 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100980 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100981 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000982 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100983 }
984
Simon Kelley3be34542004-09-11 19:12:13 +0100985 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000986 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000987 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000988 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000989 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000990 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100991
Simon Kelley44a2a312004-03-10 20:04:35 +0000992 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100993 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000994
Simon Kelleybb01cb92004-12-13 20:56:23 +0000995 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000996 return 0;
997
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100998 daemon->metrics[METRIC_DHCPDECLINE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100999 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001000
1001 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
1002 lease_prune(lease, now);
1003
Simon Kelley33820b72004-04-03 21:10:00 +01001004 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +00001005 config->addr.s_addr == option_addr(opt).s_addr)
1006 {
Simon Kelley849a8352006-06-09 21:02:31 +01001007 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +01001008 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001009 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +01001010 config->flags |= CONFIG_DECLINED;
1011 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +00001012 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001013 else
1014 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +01001015 for (; context; context = context->current)
1016 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +00001017
1018 return 0;
1019
1020 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001021 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +01001022 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001023 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001024 return 0;
1025
Simon Kelley44a2a312004-03-10 20:04:35 +00001026 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
1027 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001028 else
Simon Kelleyb8187c82005-11-26 21:46:27 +00001029 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001030
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001031 daemon->metrics[METRIC_DHCPRELEASE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001032 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001033
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001034 return 0;
1035
1036 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001037 if (ignore || have_config(config, CONFIG_DISABLE))
1038 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001039 if (option_bool(OPT_QUIET_DHCP))
1040 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001041 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001042 opt = NULL;
1043 }
1044 else
1045 {
1046 struct in_addr addr, conf;
1047
Simon Kelley1a6bca82008-07-11 11:11:42 +01001048 addr.s_addr = conf.s_addr = 0;
1049
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001050 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1051 addr = option_addr(opt);
1052
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001053 if (have_config(config, CONFIG_ADDR))
1054 {
Simon Kelley849a8352006-06-09 21:02:31 +01001055 char *addrs = inet_ntoa(config->addr);
1056
Simon Kelley9009d742008-11-14 20:04:27 +00001057 if ((ltmp = lease_find_by_addr(config->addr)) &&
1058 ltmp != lease &&
1059 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001060 {
1061 int len;
1062 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1063 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001064 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +01001065 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001066 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001067 else
1068 {
1069 struct dhcp_context *tmp;
1070 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001071 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001072 break;
1073 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001074 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
Simon Kelley849a8352006-06-09 21:02:31 +01001075 else if (have_config(config, CONFIG_DECLINED) &&
1076 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001077 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001078 else
1079 conf = config->addr;
1080 }
1081 }
1082
1083 if (conf.s_addr)
1084 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001085 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001086 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001087 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001088 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001089 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001090 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001091 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001092 else if (emac_len == 0)
1093 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001094 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001095 message = _("no address available");
1096 }
1097
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001098 daemon->metrics[METRIC_DHCPDISCOVER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001099 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001100
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001101 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001102 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001103
Simon Kelleycdeda282006-03-16 20:16:06 +00001104 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001105 {
1106 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001107 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001108 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001109
Simon Kelley4cb1b322012-02-06 14:30:41 +00001110 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001111 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001112
1113 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1114 {
1115 rapid_commit = 1;
1116 goto rapid_commit;
1117 }
1118
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001119 daemon->metrics[METRIC_DHCPOFFER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001120 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001121
Simon Kelley824af852008-02-12 20:43:05 +00001122 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001123 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001124 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001125 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001126 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001127 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001128 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001129 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001130
Simon Kelley7de060b2011-08-26 17:24:52 +01001131 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001132
1133
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001134 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001135 if (ignore || have_config(config, CONFIG_DISABLE))
1136 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001137 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001138 {
1139 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001140 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001141
Simon Kelley4011c4e2006-10-28 16:26:19 +01001142 /* send vendor and user class info for new or recreated lease */
1143 do_classes = 1;
1144
Simon Kelleybb01cb92004-12-13 20:56:23 +00001145 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001146 {
Simon Kelley3be34542004-09-11 19:12:13 +01001147 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001148 selecting = 1;
1149
Simon Kelley1a6bca82008-07-11 11:11:42 +01001150 if (override.s_addr != 0)
1151 {
1152 if (option_addr(opt).s_addr != override.s_addr)
1153 return 0;
1154 }
Simon Kelley9009d742008-11-14 20:04:27 +00001155 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001156 {
1157 for (; context; context = context->current)
1158 if (context->local.s_addr == option_addr(opt).s_addr)
1159 break;
1160
1161 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001162 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001163 /* Handle very strange configs where clients have more than one route to the server.
1164 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1165 Have to set override to make sure we echo back the correct server-id */
1166 struct irec *intr;
1167
Simon Kelley115ac3e2013-05-20 11:28:32 +01001168 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001169
1170 for (intr = daemon->interfaces; intr; intr = intr->next)
1171 if (intr->addr.sa.sa_family == AF_INET &&
1172 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1173 intr->tftp_ok)
1174 break;
1175
1176 if (intr)
1177 override = intr->addr.in.sin_addr;
1178 else
1179 {
1180 /* In auth mode, a REQUEST sent to the wrong server
1181 should be faulted, so that the client establishes
1182 communication with us, otherwise, silently ignore. */
1183 if (!option_bool(OPT_AUTHORITATIVE))
1184 return 0;
1185 message = _("wrong server-ID");
1186 }
Simon Kelley9009d742008-11-14 20:04:27 +00001187 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001188 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001189
Simon Kelley3be34542004-09-11 19:12:13 +01001190 /* If a lease exists for this host and another address, squash it. */
1191 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1192 {
1193 lease_prune(lease, now);
1194 lease = NULL;
1195 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001196 }
Simon Kelley3be34542004-09-11 19:12:13 +01001197 else
1198 {
1199 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001200 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001201 return 0;
1202
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001203 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001204 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001205 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001206 }
1207 else
1208 {
1209 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001210 /* Check existing lease for this address.
1211 We allow it to be missing if dhcp-authoritative mode
1212 as long as we can allocate the lease now - checked below.
1213 This makes for a smooth recovery from a lost lease DB */
1214 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001215 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001216 {
Simon Kelley28866e92011-02-14 20:19:14 +00001217 /* A client rebinding will broadcast the request, so we may see it even
1218 if the lease is held by another server. Just ignore it in that case.
1219 If the request is unicast to us, then somethings wrong, NAK */
1220 if (!unicast_dest)
1221 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001222 message = _("lease not found");
1223 /* ensure we broadcast NAK */
1224 unicast_dest = 0;
1225 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001226
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001227 /* desynchronise renewals */
1228 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001229 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001230 }
Simon Kelley734d5312018-03-23 23:09:53 +00001231
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001232 daemon->metrics[METRIC_DHCPREQUEST]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001233 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001234
1235 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001236 if (!message)
1237 {
1238 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001239 struct dhcp_context *tmp = NULL;
1240
1241 if (have_config(config, CONFIG_ADDR))
1242 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001243 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001244 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001245
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001246 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001247 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001248 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001249 message = _("wrong network");
1250 /* ensure we broadcast NAK */
1251 unicast_dest = 0;
1252 }
1253
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001254 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001255 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001256 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001257 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001258
Simon Kelleydfa666f2004-08-02 18:27:27 +01001259 /* Check if a new static address has been configured. Be very sure that
1260 when the client does DISCOVER, it will get the static address, otherwise
1261 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001262 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001263 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001264 (!have_config(config, CONFIG_DECLINED) ||
1265 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001266 config->addr.s_addr != mess->yiaddr.s_addr &&
1267 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001268 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001269
1270 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001271 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001272 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001273
Simon Kelley9009d742008-11-14 20:04:27 +00001274 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1275 {
1276 /* If a host is configured with more than one MAC address, it's OK to 'nix
1277 a lease from one of it's MACs to give the address to another. */
1278 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1279 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001280 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001281 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1282 inet_ntoa(ltmp->addr));
1283 lease = ltmp;
1284 }
Simon Kelley16972692006-10-16 20:04:18 +01001285 else
Simon Kelley9009d742008-11-14 20:04:27 +00001286 message = _("address in use");
1287 }
1288
1289 if (!message)
1290 {
1291 if (emac_len == 0)
1292 message = _("no unique-id");
1293
1294 else if (!lease)
1295 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001296 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001297 do_classes = 1;
1298 else
1299 message = _("no leases left");
1300 }
Simon Kelley16972692006-10-16 20:04:18 +01001301 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001302 }
Simon Kelley16972692006-10-16 20:04:18 +01001303
Simon Kelley44a2a312004-03-10 20:04:35 +00001304 if (message)
1305 {
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001306 daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
Simon Kelley734d5312018-03-23 23:09:53 +00001307 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1308
1309 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1310 if (rapid_commit)
1311 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001312
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001313 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001314 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001315 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001316 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001317 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001318 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1319 a distant subnet which unicast a REQ to us won't work. */
1320 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1321 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1322 {
1323 mess->flags |= htons(0x8000); /* broadcast */
1324 mess->ciaddr.s_addr = 0;
1325 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001326 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001327 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001328 {
Simon Kelley316e2732010-01-22 20:16:09 +00001329 if (context->netid.net)
1330 {
1331 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001332 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001333 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001334
Simon Kelley4cb1b322012-02-06 14:30:41 +00001335 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001336
Simon Kelleydcffad22012-04-24 15:25:18 +01001337 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001338 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001339 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001340 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001341
Simon Kelleydcffad22012-04-24 15:25:18 +01001342#ifdef HAVE_SCRIPT
1343 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001344 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001345 struct dhcp_netid *n;
1346
1347 if (mess->giaddr.s_addr)
1348 lease->giaddr = mess->giaddr;
1349
1350 free(lease->extradata);
1351 lease->extradata = NULL;
1352 lease->extradata_size = lease->extradata_len = 0;
1353
1354 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1355 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1356 add_extradata_opt(lease, oui);
1357 add_extradata_opt(lease, serial);
1358 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001359
1360 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1361 {
1362 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1363 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1364 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1365 }
1366 else
1367 {
1368 add_extradata_opt(lease, NULL);
1369 add_extradata_opt(lease, NULL);
1370 add_extradata_opt(lease, NULL);
1371 }
1372
ZHAO Yuf89cae32016-12-22 22:32:31 +00001373 /* DNSMASQ_REQUESTED_OPTIONS */
1374 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1375 {
1376 int len = option_len(opt);
1377 unsigned char *rop = option_ptr(opt, 0);
1378 char *q = daemon->namebuff;
1379 int i;
1380 for (i = 0; i < len; i++)
1381 {
1382 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1383 }
1384 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1385 }
1386 else
1387 {
1388 add_extradata_opt(lease, NULL);
1389 }
1390
Simon Kelleydcffad22012-04-24 15:25:18 +01001391 /* space-concat tag set */
1392 if (!tagif_netid)
1393 add_extradata_opt(lease, NULL);
1394 else
1395 for (n = tagif_netid; n; n = n->next)
1396 {
1397 struct dhcp_netid *n1;
1398 /* kill dupes */
1399 for (n1 = n->next; n1; n1 = n1->next)
1400 if (strcmp(n->net, n1->net) == 0)
1401 break;
1402 if (!n1)
1403 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1404 }
1405
1406 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1407 {
1408 int len = option_len(opt);
1409 unsigned char *ucp = option_ptr(opt, 0);
1410 /* If the user-class option started as counted strings, the first byte will be zero. */
1411 if (len != 0 && ucp[0] == 0)
1412 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001413 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001414 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001415 }
Simon Kelley316e2732010-01-22 20:16:09 +00001416#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001417 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001418
1419 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1420 {
1421 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001422 hostname = client_hostname;
1423 hostname_auth = 1;
1424 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001425
Simon Kelley824af852008-02-12 20:43:05 +00001426 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001427 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001428
Simon Kelley832af0b2007-01-21 20:01:28 +00001429 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1430 if (!hostname_auth)
1431 {
1432 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001433 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001434 break;
1435 if (id_list)
1436 hostname = NULL;
1437 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001438
1439 /* Last ditch, if configured, generate hostname from mac address */
1440 if (!hostname && emac_len != 0)
1441 {
1442 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1443 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1444 break;
1445 if (id_list)
1446 {
1447 int i;
1448
1449 hostname = daemon->dhcp_buff;
1450 /* buffer is 256 bytes, 3 bytes per octet */
1451 for (i = 0; (i < emac_len) && (i < 80); i++)
1452 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1453 hostname = daemon->dhcp_buff;
1454 }
1455 }
1456
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001457 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001458 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001459
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001460 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001461 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001462
1463 if (override.s_addr != 0)
1464 lease->override = override;
1465 else
1466 override = lease->override;
1467
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001468 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001469 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001470
Simon Kelleya9df0e32017-04-28 22:43:00 +01001471 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001472 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001473 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001474 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001475 if (rapid_commit)
1476 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1477 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001478 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001479 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001480
Simon Kelley7de060b2011-08-26 17:24:52 +01001481 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001482
1483 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001484 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001485 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001486
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001487 daemon->metrics[METRIC_DHCPINFORM]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001488 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001489
Simon Kelley73a08a22009-02-05 20:28:08 +00001490 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001491 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001492
1493 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001494 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001495
Simon Kelley5aabfc72007-08-29 11:24:47 +01001496 /* Find a least based on IP address if we didn't
1497 get one from MAC address/client-d */
1498 if (!lease &&
1499 (lease = lease_find_by_addr(mess->ciaddr)) &&
1500 lease->hostname)
1501 hostname = lease->hostname;
1502
Simon Kelley0f371f92013-07-27 15:15:38 +01001503 if (!hostname)
1504 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001505
Simon Kelley73a08a22009-02-05 20:28:08 +00001506 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001507 {
1508 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001509 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001510 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001511
Simon Kelley4cb1b322012-02-06 14:30:41 +00001512 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001513
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001514 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001515 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001516
Simon Kelley3927da42008-07-20 15:10:39 +01001517 if (lease)
1518 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001519 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001520 if (override.s_addr != 0)
1521 lease->override = override;
1522 else
1523 override = lease->override;
1524 }
1525
Simon Kelleya9df0e32017-04-28 22:43:00 +01001526 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001527 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001528 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001529
1530 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1531 we supply a utility which makes DHCPINFORM requests to get this information.
1532 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1533 which won't be true for ordinary clients, but will be true for the
1534 dhcp_lease_time utility. */
1535 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1536 {
1537 if (lease->expires == 0)
1538 time = 0xffffffff;
1539 else
1540 time = (unsigned int)difftime(lease->expires, now);
1541 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1542 }
1543
Simon Kelley9009d742008-11-14 20:04:27 +00001544 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001545 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001546
Simon Kelley5aabfc72007-08-29 11:24:47 +01001547 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001548 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001549 }
1550
1551 return 0;
1552}
1553
Simon Kelley6b010842007-02-12 20:32:07 +00001554/* find a good value to use as MAC address for logging and address-allocation hashing.
1555 This is normally just the chaddr field from the DHCP packet,
1556 but eg Firewire will have hlen == 0 and use the client-id instead.
1557 This could be anything, but will normally be EUI64 for Firewire.
1558 We assume that if the first byte of the client-id equals the htype byte
1559 then the client-id is using the usual encoding and use the rest of the
1560 client-id: if not we can use the whole client-id. This should give
1561 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001562unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001563 int clid_len, unsigned char *clid, int *len_out)
1564{
1565 if (hwlen == 0 && clid && clid_len > 3)
1566 {
1567 if (clid[0] == hwtype)
1568 {
1569 *len_out = clid_len - 1 ;
1570 return clid + 1;
1571 }
1572
1573#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1574 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1575 {
1576 *len_out = clid_len - 1 ;
1577 return clid + 1;
1578 }
1579#endif
1580
1581 *len_out = clid_len;
1582 return clid;
1583 }
1584
1585 *len_out = hwlen;
1586 return hwaddr;
1587}
1588
Simon Kelley824af852008-02-12 20:43:05 +00001589static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001590{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001591 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001592
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001593 if (opt)
1594 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001595 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001596 if (req_time < 120 )
1597 req_time = 120; /* sanity */
1598 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1599 time = req_time;
1600 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001601
1602 return time;
1603}
1604
Simon Kelley73a08a22009-02-05 20:28:08 +00001605static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001606{
Simon Kelley73a08a22009-02-05 20:28:08 +00001607 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001608 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001609 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001610 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001611 else
1612 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001613}
1614
Simon Kelleyf2621c72007-04-29 19:47:21 +01001615static int sanitise(unsigned char *opt, char *buf)
1616{
1617 char *p;
1618 int i;
1619
1620 *buf = 0;
1621
1622 if (!opt)
1623 return 0;
1624
Simon Kelley1a6bca82008-07-11 11:11:42 +01001625 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001626
1627 for (i = option_len(opt); i > 0; i--)
1628 {
1629 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001630 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001631 *buf++ = c;
1632 }
1633 *buf = 0; /* add terminator */
1634
1635 return 1;
1636}
1637
Simon Kelley316e2732010-01-22 20:16:09 +00001638#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001639static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1640{
1641 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001642 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001643 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001644 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001645}
1646#endif
1647
Simon Kelley5aabfc72007-08-29 11:24:47 +01001648static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001649 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001650{
Simon Kelley16972692006-10-16 20:04:18 +01001651 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001652
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001653 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001654 return;
1655
Simon Kelley16972692006-10-16 20:04:18 +01001656 /* addr may be misaligned */
1657 if (addr)
1658 memcpy(&a, addr, sizeof(a));
1659
Simon Kelley7622fc02009-06-04 20:32:05 +01001660 print_mac(daemon->namebuff, ext_mac, mac_len);
1661
Simon Kelley28866e92011-02-14 20:19:14 +00001662 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001663 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001664 ntohl(xid),
1665 type,
1666 interface,
1667 addr ? inet_ntoa(a) : "",
1668 addr ? " " : "",
1669 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001670 string ? string : "",
1671 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001672 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001673 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001674 type,
1675 interface,
1676 addr ? inet_ntoa(a) : "",
1677 addr ? " " : "",
1678 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001679 string ? string : "",
1680 err ? err : "");
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001681
1682#ifdef HAVE_UBUS
1683 if (!strcmp(type, "DHCPACK"))
1684 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1685 else if (!strcmp(type, "DHCPRELEASE"))
1686 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1687#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001688}
1689
Simon Kelley7622fc02009-06-04 20:32:05 +01001690static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001691{
1692 while (*start != OPTION_END)
1693 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001694 char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
Simon Kelley7622fc02009-06-04 20:32:05 +01001695
Simon Kelley4cb1b322012-02-06 14:30:41 +00001696 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1697 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001698 start += start[1] + 2;
1699 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001700}
1701
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001702static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001703{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001704 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001705 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001706 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001707 return NULL;
1708 else if (*p == OPTION_END)
1709 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001710 else if (*p == OPTION_PAD)
1711 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001712 else
1713 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001714 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001715 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001716 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001717 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001718 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001719 return NULL; /* malformed packet */
1720 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001721 return p;
1722 p += opt_len + 2;
1723 }
1724 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001725}
1726
Simon Kelleycdeda282006-03-16 20:16:06 +00001727static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001728{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001729 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001730
Simon Kelley3be34542004-09-11 19:12:13 +01001731 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001732 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1733 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001734
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001735 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001736 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001737 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001738
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001739 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001740 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001741 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1742 return ret;
1743
1744 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001745 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001746 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1747 return ret;
1748
1749 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001750}
1751
Simon Kelley4cb1b322012-02-06 14:30:41 +00001752static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001753{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001754 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001755 /* struct in_addr is network byte order */
1756 struct in_addr ret;
1757
Simon Kelley4cb1b322012-02-06 14:30:41 +00001758 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001759
1760 return ret;
1761}
1762
Simon Kelley7622fc02009-06-04 20:32:05 +01001763static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001764{
1765 /* this worries about unaligned data and byte order */
1766 unsigned int ret = 0;
1767 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001768 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001769
1770 for (i = 0; i < size; i++)
1771 ret = (ret << 8) | *p++;
1772
1773 return ret;
1774}
1775
1776static unsigned char *dhcp_skip_opts(unsigned char *start)
1777{
1778 while (*start != 0)
1779 start += start[1] + 2;
1780 return start;
1781}
1782
1783/* only for use when building packet: doesn't check for bad data. */
1784static unsigned char *find_overload(struct dhcp_packet *mess)
1785{
1786 unsigned char *p = &mess->options[0] + sizeof(u32);
1787
1788 while (*p != 0)
1789 {
1790 if (*p == OPTION_OVERLOAD)
1791 return p;
1792 p += p[1] + 2;
1793 }
1794 return NULL;
1795}
1796
Simon Kelley7de060b2011-08-26 17:24:52 +01001797static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1798{
1799 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1800 unsigned char *overload;
1801 size_t ret;
1802
1803 /* move agent_id back down to the end of the packet */
1804 if (agent_id)
1805 {
1806 memmove(p, agent_id, real_end - agent_id);
1807 p += real_end - agent_id;
1808 memset(p, 0, real_end - p); /* in case of overlap */
1809 }
1810
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001811 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001812 overload = find_overload(mess);
1813
1814 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001815 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001816 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001817 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001818 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001819 }
Simon Kelley28866e92011-02-14 20:19:14 +00001820 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001821 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1822
1823 if (overload && (option_uint(overload, 0, 1) & 2))
1824 {
1825 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001826 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001827 log_options(mess->sname, mess->xid);
1828 }
Simon Kelley28866e92011-02-14 20:19:14 +00001829 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001830 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1831
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001832
1833 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001834
Simon Kelley28866e92011-02-14 20:19:14 +00001835 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001836 {
1837 if (mess->siaddr.s_addr != 0)
1838 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1839
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001840 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1841 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1842
Simon Kelley7622fc02009-06-04 20:32:05 +01001843 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1844 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001845
1846 ret = (size_t)(p - (unsigned char *)mess);
1847
1848 if (ret < MIN_PACKETSZ)
1849 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001850
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001851 return ret;
1852}
1853
1854static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1855{
1856 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1857
1858 if (p + len + 3 >= end)
1859 /* not enough space in options area, try and use overload, if poss */
1860 {
1861 unsigned char *overload;
1862
1863 if (!(overload = find_overload(mess)) &&
1864 (mess->file[0] == 0 || mess->sname[0] == 0))
1865 {
1866 /* attempt to overload fname and sname areas, we've reserved space for the
1867 overflow option previuously. */
1868 overload = p;
1869 *(p++) = OPTION_OVERLOAD;
1870 *(p++) = 1;
1871 }
1872
1873 p = NULL;
1874
1875 /* using filename field ? */
1876 if (overload)
1877 {
1878 if (mess->file[0] == 0)
1879 overload[2] |= 1;
1880
1881 if (overload[2] & 1)
1882 {
1883 p = dhcp_skip_opts(mess->file);
1884 if (p + len + 3 >= mess->file + sizeof(mess->file))
1885 p = NULL;
1886 }
1887
1888 if (!p)
1889 {
1890 /* try to bring sname into play (it may be already) */
1891 if (mess->sname[0] == 0)
1892 overload[2] |= 2;
1893
1894 if (overload[2] & 2)
1895 {
1896 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001897 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001898 p = NULL;
1899 }
1900 }
1901 }
1902
1903 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001904 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001905 }
1906
1907 if (p)
1908 {
1909 *(p++) = opt;
1910 *(p++) = len;
1911 }
1912
1913 return p;
1914}
1915
1916static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1917{
1918 int i;
1919 unsigned char *p = free_space(mess, end, opt, len);
1920
1921 if (p)
1922 for (i = 0; i < len; i++)
1923 *(p++) = val >> (8 * (len - (i + 1)));
1924}
1925
1926static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1927 char *string, int null_term)
1928{
1929 unsigned char *p;
1930 size_t len = strlen(string);
1931
1932 if (null_term && len != 255)
1933 len++;
1934
1935 if ((p = free_space(mess, end, opt, len)))
1936 memcpy(p, string, len);
1937}
1938
1939/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001940static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001941{
1942 int len = opt->len;
1943
1944 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1945 len++;
1946
1947 if (p && len != 0)
1948 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001949 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001950 {
1951 int j;
1952 struct in_addr *a = (struct in_addr *)opt->val;
1953 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1954 {
1955 /* zero means "self" (but not in vendorclass options.) */
1956 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001957 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001958 else
1959 memcpy(p, a, INADDRSZ);
1960 p += INADDRSZ;
1961 }
1962 }
1963 else
Simon Kelley625ac282013-07-02 21:19:32 +01001964 /* empty string may be extended to "\0" by null_term */
1965 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001966 }
1967 return len;
1968}
Simon Kelley7622fc02009-06-04 20:32:05 +01001969
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001970static int in_list(unsigned char *list, int opt)
1971{
1972 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001973
1974 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001975 if (!list)
1976 return 1;
1977
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001978 for (i = 0; list[i] != OPTION_END; i++)
1979 if (opt == list[i])
1980 return 1;
1981
1982 return 0;
1983}
1984
Simon Kelley7de060b2011-08-26 17:24:52 +01001985static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001986{
Simon Kelley7de060b2011-08-26 17:24:52 +01001987 struct dhcp_opt *opts;
1988
1989 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1990 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1991 return opts;
1992
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001993 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001994}
1995
Simon Kelley6b010842007-02-12 20:32:07 +00001996/* mark vendor-encapsulated options which match the client-supplied or
1997 config-supplied vendor class */
1998static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1999{
2000 for (; dopt; dopt = dopt->next)
2001 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002002 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00002003 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00002004 {
2005 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00002006 if (dopt->u.vendor_class)
2007 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00002008 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00002009 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00002010 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002011 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00002012 break;
2013 }
2014 }
2015 }
2016}
2017
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002018static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
2019 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00002020{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002021 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01002022 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00002023 unsigned char *p;
2024
2025 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01002026 for (enc_len = 0, start = opt; opt; opt = opt->next)
2027 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002028 {
2029 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002030 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00002031 if (enc_len + new <= 255)
2032 enc_len += new;
2033 else
2034 {
2035 p = free_space(mess, end, encap, enc_len);
2036 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002037 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00002038 {
2039 len = do_opt(start, p + 2, NULL, null_term);
2040 *(p++) = start->opt;
2041 *(p++) = len;
2042 p += len;
2043 }
2044 enc_len = new;
2045 start = opt;
2046 }
2047 }
2048
2049 if (enc_len != 0 &&
2050 (p = free_space(mess, end, encap, enc_len + 1)))
2051 {
2052 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002053 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002054 {
2055 len = do_opt(start, p + 2, NULL, null_term);
2056 *(p++) = start->opt;
2057 *(p++) = len;
2058 p += len;
2059 }
2060 *p = OPTION_END;
2061 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002062
2063 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002064}
2065
Simon Kelley7622fc02009-06-04 20:32:05 +01002066static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002067{
Simon Kelley7622fc02009-06-04 20:32:05 +01002068 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002069
Simon Kelley7622fc02009-06-04 20:32:05 +01002070 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2071 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2072 memcpy(p, uuid, 17);
2073}
2074
2075static int prune_vendor_opts(struct dhcp_netid *netid)
2076{
2077 int force = 0;
2078 struct dhcp_opt *opt;
2079
2080 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2081 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2082 if (opt->flags & DHOPT_VENDOR_MATCH)
2083 {
2084 if (!match_netid(opt->netid, netid, 1))
2085 opt->flags &= ~DHOPT_VENDOR_MATCH;
2086 else if (opt->flags & DHOPT_FORCE)
2087 force = 1;
2088 }
2089 return force;
2090}
2091
Simon Kelley8628cd62016-05-10 17:31:48 +01002092
2093/* Many UEFI PXE implementations have badly broken menu code.
2094 If there's exactly one relevant menu item, we abandon the menu system,
2095 and jamb the data direct into the DHCP file, siaddr and sname fields.
2096 Note that in this case, we have to assume that layer zero would be requested
2097 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002098static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe)
Simon Kelley8628cd62016-05-10 17:31:48 +01002099{
2100 struct pxe_service *service, *found;
2101
2102 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002103 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002104 return 0;
2105
2106 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002107 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002108 {
2109 if (found)
2110 return 0; /* More than one relevant menu item */
2111
2112 found = service;
2113 }
2114
2115 if (!found)
2116 return 0; /* No relevant menu items. */
2117
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002118 if (!pxe)
2119 return 1;
2120
Simon Kelley8628cd62016-05-10 17:31:48 +01002121 if (found->sname)
2122 {
2123 mess->siaddr = a_record_from_hosts(found->sname, now);
2124 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2125 }
2126 else
2127 {
2128 if (found->server.s_addr != 0)
2129 mess->siaddr = found->server;
2130 else
2131 mess->siaddr = local;
2132
2133 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2134 }
2135
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002136 snprintf((char *)mess->file, sizeof(mess->file),
2137 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2138
Simon Kelley8628cd62016-05-10 17:31:48 +01002139 return 1;
2140}
2141
Simon Kelley751d6f42012-02-10 15:24:51 +00002142static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002143{
2144#define NUM_OPTS 4
2145
2146 unsigned char *p, *q;
2147 struct pxe_service *service;
2148 static struct dhcp_opt *o, *ret;
2149 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002150 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002151
2152 /* We pass back references to these, hence they are declared static */
2153 static unsigned char discovery_control;
2154 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2155 static struct dhcp_opt *fake_opts = NULL;
2156
Simon Kelley316e2732010-01-22 20:16:09 +00002157 /* Disable multicast, since we don't support it, and broadcast
2158 unless we need it */
2159 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002160
2161 ret = daemon->dhcp_opts;
2162
2163 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2164 return ret;
2165
2166 for (i = 0; i < NUM_OPTS; i++)
2167 {
2168 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2169 fake_opts[i].netid = NULL;
2170 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2171 }
2172
2173 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2174 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002175 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002176
2177 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2178 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2179 {
2180 size_t len = strlen(service->menu);
2181 /* opt 43 max size is 255. encapsulated option has type and length
2182 bytes, so its max size is 253. */
2183 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2184 {
2185 *(p++) = service->type >> 8;
2186 *(p++) = service->type;
2187 *(p++) = len;
2188 memcpy(p, service->menu, len);
2189 p += len;
2190 i++;
2191 }
2192 else
2193 {
2194 toobig:
2195 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2196 return daemon->dhcp_opts;
2197 }
2198
Simon Kelley751d6f42012-02-10 15:24:51 +00002199 boot_server = service->basename ? local :
2200 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2201
Simon Kelley316e2732010-01-22 20:16:09 +00002202 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002203 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002204 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002205 goto toobig;
2206
2207 /* Boot service with known address - give it */
2208 *(q++) = service->type >> 8;
2209 *(q++) = service->type;
2210 *(q++) = 1;
2211 /* dest misaligned */
2212 memcpy(q, &boot_server.s_addr, INADDRSZ);
2213 q += INADDRSZ;
2214 }
2215 else if (service->type != 0)
2216 /* We don't know the server for a service type, so we'll
2217 allow the client to broadcast for it */
2218 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002219 }
2220
2221 /* if no prompt, wait forever if there's a choice */
2222 fake_prompt[0] = (i > 1) ? 255 : 0;
2223
2224 if (i == 0)
2225 discovery_control = 8; /* no menu - just use use mess->filename */
2226 else
2227 {
2228 ret = &fake_opts[j--];
2229 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2230 ret->val = (unsigned char *)daemon->dhcp_buff;
2231 ret->opt = SUBOPT_PXE_MENU;
2232
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002233 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002234 {
2235 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002236 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2237 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002238 ret->opt = SUBOPT_PXE_SERVERS;
2239 }
2240 }
2241
2242 for (o = daemon->dhcp_opts; o; o = o->next)
2243 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2244 break;
2245
2246 if (!o)
2247 {
2248 ret = &fake_opts[j--];
2249 ret->len = sizeof(fake_prompt);
2250 ret->val = fake_prompt;
2251 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2252 }
2253
Simon Kelley316e2732010-01-22 20:16:09 +00002254 ret = &fake_opts[j--];
2255 ret->len = 1;
2256 ret->opt = SUBOPT_PXE_DISCOVERY;
2257 ret->val= &discovery_control;
2258
Simon Kelley7622fc02009-06-04 20:32:05 +01002259 return ret;
2260}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002261
2262static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002263{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002264 memset(mess->sname, 0, sizeof(mess->sname));
2265 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002266 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002267 mess->siaddr.s_addr = 0;
2268}
Simon Kelleycdeda282006-03-16 20:16:06 +00002269
Simon Kelley7622fc02009-06-04 20:32:05 +01002270struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002271{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002272 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002273
2274 /* decide which dhcp-boot option we're using */
2275 for (boot = daemon->boot_config; boot; boot = boot->next)
2276 if (match_netid(boot->netid, netid, 0))
2277 break;
2278 if (!boot)
2279 /* No match, look for one without a netid */
2280 for (boot = daemon->boot_config; boot; boot = boot->next)
2281 if (match_netid(boot->netid, netid, 1))
2282 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002283
2284 return boot;
2285}
2286
2287static void do_options(struct dhcp_context *context,
2288 struct dhcp_packet *mess,
2289 unsigned char *end,
2290 unsigned char *req_options,
2291 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002292 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002293 struct dhcp_netid *netid,
2294 struct in_addr subnet_addr,
2295 unsigned char fqdn_flags,
2296 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002297 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002298 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002299 time_t now,
2300 unsigned int lease_time,
2301 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002302{
2303 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2304 struct dhcp_boot *boot;
2305 unsigned char *p;
2306 int i, len, force_encap = 0;
2307 unsigned char f0 = 0, s0 = 0;
2308 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002309 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002310 struct dhcp_netid *tagif;
2311 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002312
Simon Kelley4cb1b322012-02-06 14:30:41 +00002313 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002314 if (context)
2315 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002316 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002317
Simon Kelley7622fc02009-06-04 20:32:05 +01002318 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002319 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002320 {
2321 char *q = daemon->namebuff;
2322 for (i = 0; req_options[i] != OPTION_END; i++)
2323 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002324 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002325 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2326 "%d%s%s%s",
2327 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002328 strlen(s) != 0 ? ":" : "",
2329 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002330 req_options[i+1] == OPTION_END ? "" : ", ");
2331 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2332 {
2333 q = daemon->namebuff;
2334 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2335 }
2336 }
2337 }
2338
Simon Kelley7de060b2011-08-26 17:24:52 +01002339 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2340 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2341 break;
2342 if (id_list)
2343 mess->flags |= htons(0x8000); /* force broadcast */
2344
Simon Kelley73a08a22009-02-05 20:28:08 +00002345 if (context)
2346 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002347
2348 /* See if we can send the boot stuff as options.
2349 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002350 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002351 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002352 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002353 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002354 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002355 {
2356 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002357 {
Simon Kelley28866e92011-02-14 20:19:14 +00002358 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002359 req_options &&
2360 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002361 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2362 else
Simon Kelley824af852008-02-12 20:43:05 +00002363 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002364 }
2365
2366 if (boot->file)
2367 {
Simon Kelley28866e92011-02-14 20:19:14 +00002368 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002369 req_options &&
2370 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002371 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2372 else
Simon Kelley824af852008-02-12 20:43:05 +00002373 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002374 }
2375
Simon Kelley7de060b2011-08-26 17:24:52 +01002376 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002377 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002378 else if (boot->tftp_sname)
2379 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002380 }
Simon Kelley824af852008-02-12 20:43:05 +00002381 else
2382 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002383 they're not explicitly asked for as options. OPTION_END is used
2384 as an internal way to specify siaddr without using dhcp-boot, for use in
2385 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002386 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002387 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002388 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002389 {
2390 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2391 done_file = 1;
2392 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002393
Simon Kelley824af852008-02-12 20:43:05 +00002394 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002395 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002396 {
2397 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2398 done_server = 1;
2399 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002400
Simon Kelley7de060b2011-08-26 17:24:52 +01002401 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002402 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002403 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002404
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002405 /* We don't want to do option-overload for BOOTP, so make the file and sname
2406 fields look like they are in use, even when they aren't. This gets restored
2407 at the end of this function. */
2408
Simon Kelley28866e92011-02-14 20:19:14 +00002409 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002410 {
2411 f0 = mess->file[0];
2412 mess->file[0] = 1;
2413 s0 = mess->sname[0];
2414 mess->sname[0] = 1;
2415 }
2416
2417 /* At this point, if mess->sname or mess->file are zeroed, they are available
2418 for option overload, reserve space for the overload option. */
2419 if (mess->file[0] == 0 || mess->sname[0] == 0)
2420 end -= 3;
2421
Simon Kelley3be34542004-09-11 19:12:13 +01002422 /* rfc3011 says this doesn't need to be in the requested options list. */
2423 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002424 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002425
2426 if (lease_time != 0xffffffff)
2427 {
2428 unsigned int t1val = lease_time/2;
2429 unsigned int t2val = (lease_time*7)/8;
2430 unsigned int hval;
2431
2432 /* If set by user, sanity check, so not longer than lease. */
2433 if ((opt = option_find2(OPTION_T1)))
2434 {
2435 hval = ntohl(*((unsigned int *)opt->val));
2436 if (hval < lease_time && hval > 2)
2437 t1val = hval;
2438 }
2439
2440 if ((opt = option_find2(OPTION_T2)))
2441 {
2442 hval = ntohl(*((unsigned int *)opt->val));
2443 if (hval < lease_time && hval > 2)
2444 t2val = hval;
2445 }
2446
Simon Kelley7c0f2542015-05-14 21:16:18 +01002447 /* ensure T1 is still < T2 */
2448 if (t2val <= t1val)
2449 t1val = t2val - 1;
2450
Simon Kelleyca85a282015-05-13 22:33:04 +01002451 while (fuzz > (t1val/8))
2452 fuzz = fuzz/2;
2453
2454 t1val -= fuzz;
2455 t2val -= fuzz;
2456
Simon Kelleyca85a282015-05-13 22:33:04 +01002457 option_put(mess, end, OPTION_T1, 4, t1val);
2458 option_put(mess, end, OPTION_T2, 4, t2val);
2459 }
2460
Simon Kelley73a08a22009-02-05 20:28:08 +00002461 /* replies to DHCPINFORM may not have a valid context */
2462 if (context)
2463 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002464 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002465 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2466
2467 /* May not have a "guessed" broadcast address if we got no packets via a relay
2468 from this net yet (ie just unicast renewals after a restart */
2469 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002470 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002471 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2472
2473 /* Same comments as broadcast apply, and also may not be able to get a sensible
2474 default when using subnet select. User must configure by steam in that case. */
2475 if (context->router.s_addr &&
2476 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002477 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002478 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2479
Simon Kelleya21e27b2013-02-17 16:41:35 +00002480 if (daemon->port == NAMESERVER_PORT &&
2481 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002482 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002483 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2484 }
Simon Kelley3be34542004-09-11 19:12:13 +01002485
Simon Kelley9009d742008-11-14 20:04:27 +00002486 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002487 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002488 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002489
Simon Kelley824af852008-02-12 20:43:05 +00002490 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002491 if (hostname)
2492 {
Simon Kelley824af852008-02-12 20:43:05 +00002493 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002494 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002495 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002496
2497 if (fqdn_flags != 0)
2498 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002499 len = strlen(hostname) + 3;
2500
Simon Kelley3d8df262005-08-29 12:19:27 +01002501 if (fqdn_flags & 0x04)
2502 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002503 else if (null_term)
2504 len++;
2505
Simon Kelley9009d742008-11-14 20:04:27 +00002506 if (domain)
2507 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002508 else if (fqdn_flags & 0x04)
2509 len--;
2510
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002511 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002512 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002513 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002514 *(p++) = 255;
2515 *(p++) = 255;
2516
2517 if (fqdn_flags & 0x04)
2518 {
Simon Kelley0549c732017-09-25 18:17:11 +01002519 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002520 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002521 {
Simon Kelley0549c732017-09-25 18:17:11 +01002522 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002523 *p++ = 0;
2524 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002525 }
2526 else
2527 {
2528 memcpy(p, hostname, strlen(hostname));
2529 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002530 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002531 {
2532 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002533 memcpy(p, domain, strlen(domain));
2534 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002535 }
2536 if (null_term)
2537 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002538 }
2539 }
2540 }
2541 }
2542
Simon Kelley6b010842007-02-12 20:32:07 +00002543 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002544 {
Simon Kelley824af852008-02-12 20:43:05 +00002545 int optno = opt->opt;
2546
Simon Kelley7de060b2011-08-26 17:24:52 +01002547 /* netids match and not encapsulated? */
2548 if (!(opt->flags & DHOPT_TAGOK))
2549 continue;
2550
Simon Kelley6b010842007-02-12 20:32:07 +00002551 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002552 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002553 continue;
2554
Simon Kelleyca85a282015-05-13 22:33:04 +01002555 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002556 if (optno == OPTION_CLIENT_FQDN ||
2557 optno == OPTION_MAXMESSAGE ||
2558 optno == OPTION_OVERLOAD ||
2559 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002560 optno == OPTION_END ||
2561 optno == OPTION_T1 ||
2562 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002563 continue;
2564
2565 if (optno == OPTION_SNAME && done_server)
2566 continue;
2567
2568 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002569 continue;
2570
Simon Kelley33820b72004-04-03 21:10:00 +01002571 /* For the options we have default values on
2572 dhc-option=<optionno> means "don't include this option"
2573 not "include a zero-length option" */
2574 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002575 (optno == OPTION_NETMASK ||
2576 optno == OPTION_BROADCAST ||
2577 optno == OPTION_ROUTER ||
2578 optno == OPTION_DNSSERVER ||
2579 optno == OPTION_DOMAINNAME ||
2580 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002581 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002582
2583 /* vendor-class comes from elsewhere for PXE */
2584 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2585 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002586
Simon Kelley7622fc02009-06-04 20:32:05 +01002587 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002588 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002589 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002590
Simon Kelley824af852008-02-12 20:43:05 +00002591 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002592 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002593 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002594 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2595
Simon Kelley6b010842007-02-12 20:32:07 +00002596 /* If we send a vendor-id, revisit which vendor-ops we consider
2597 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002598 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002599 {
2600 match_vendor_opts(p - 2, config_opts);
2601 done_vendor_class = 1;
2602 }
Simon Kelley6b010842007-02-12 20:32:07 +00002603 }
2604 }
2605
Simon Kelley73a08a22009-02-05 20:28:08 +00002606 /* Now send options to be encapsulated in arbitrary options,
2607 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002608 Also handle vendor-identifying vendor-encapsulated options,
2609 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002610 The may be more that one "outer" to do, so group
2611 all the options which match each outer in turn. */
2612 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002613 opt->flags &= ~DHOPT_ENCAP_DONE;
2614
2615 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002616 {
2617 int flags;
2618
2619 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2620 {
2621 int found = 0;
2622 struct dhcp_opt *o;
2623
2624 if (opt->flags & DHOPT_ENCAP_DONE)
2625 continue;
2626
2627 for (len = 0, o = config_opts; o; o = o->next)
2628 {
2629 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2630
2631 o->flags &= ~DHOPT_ENCAP_MATCH;
2632
2633 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2634 continue;
2635
2636 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002637 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002638 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2639 {
2640 o->flags |= DHOPT_ENCAP_MATCH;
2641 found = 1;
2642 len += do_opt(o, NULL, NULL, 0) + 2;
2643 }
2644 }
2645
2646 if (found)
2647 {
2648 if (flags & DHOPT_ENCAPSULATE)
2649 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2650 else if (len > 250)
2651 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2652 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2653 {
2654 int swap_ent = htonl(opt->u.encap);
2655 memcpy(p, &swap_ent, 4);
2656 p += 4;
2657 *(p++) = len;
2658 for (o = config_opts; o; o = o->next)
2659 if (o->flags & DHOPT_ENCAP_MATCH)
2660 {
2661 len = do_opt(o, p + 2, NULL, 0);
2662 *(p++) = o->opt;
2663 *(p++) = len;
2664 p += len;
2665 }
2666 }
2667 }
2668 }
2669 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002670
Simon Kelley7de060b2011-08-26 17:24:52 +01002671 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002672
Simon Kelley316e2732010-01-22 20:16:09 +00002673 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002674 {
2675 pxe_misc(mess, end, uuid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002676 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002677 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002678 }
2679
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002680 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2681 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2682 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2683 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2684 /* If we send vendor encapsulated options, and haven't already sent option 60,
2685 echo back the value we got from the client. */
2686 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2687
Simon Kelley7622fc02009-06-04 20:32:05 +01002688 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002689 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002690 {
2691 mess->file[0] = f0;
2692 mess->sname[0] = s0;
2693 }
2694}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002695
Floris Bos503c6092017-04-09 23:07:13 +01002696static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2697{
2698 struct delay_config *delay_conf;
2699
2700 /* Decide which delay_config option we're using */
2701 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2702 if (match_netid(delay_conf->netid, netid, 0))
2703 break;
2704
2705 if (!delay_conf)
2706 /* No match, look for one without a netid */
2707 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2708 if (match_netid(delay_conf->netid, netid, 1))
2709 break;
2710
2711 if (delay_conf)
2712 {
2713 if (!option_bool(OPT_QUIET_DHCP))
2714 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2715 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2716 }
2717}
2718
Simon Kelley7622fc02009-06-04 20:32:05 +01002719#endif
2720
2721
2722
2723
2724
2725
2726