blob: 56dc3d103741baeb68a730f0ce15a10338a2f885 [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 Kelley0fdf3c12018-10-05 23:35:54 +0100703 if (client_hostname && option_bool(OPT_LOG_OPTS))
704 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
Simon Kelleyc8226202018-08-08 23:46:03 +0100705
Simon Kelley7622fc02009-06-04 20:32:05 +0100706
Simon Kelley3d8df262005-08-29 12:19:27 +0100707 if (have_config(config, CONFIG_NAME))
708 {
709 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000710 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000711 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000712 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100713 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000714 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100715 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100716 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100717 {
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100718 struct dhcp_match_name *m;
719 size_t nl;
720
Simon Kelley9009d742008-11-14 20:04:27 +0000721 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100722
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100723 if ((nl = strlen(client_hostname)) != 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100724 {
725 hostname = client_hostname;
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100726
Simon Kelley5aabfc72007-08-29 11:24:47 +0100727 if (!config)
728 {
729 /* Search again now we have a hostname.
730 Only accept configs without CLID and HWADDR here, (they won't match)
731 to avoid impersonation by name. */
732 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
733 mess->chaddr, mess->hlen,
734 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000735 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000736 {
737 config = new;
738 /* set "known" tag for known hosts */
739 known_id.net = "known";
740 known_id.next = netid;
741 netid = &known_id;
742 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100743 }
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100744
745 for (m = daemon->dhcp_name_match; m; m = m->next)
746 {
747 size_t ml = strlen(m->name);
748 char save = 0;
749
750 if (nl < ml)
751 continue;
752 if (nl > ml)
753 {
754 save = client_hostname[ml];
755 client_hostname[ml] = 0;
756 }
757
758 if (hostname_isequal(client_hostname, m->name) &&
759 (save == 0 || m->wildcard))
760 {
761 m->netid->next = netid;
762 netid = m->netid;
763 }
764
765 if (save != 0)
766 client_hostname[ml] = save;
767 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100768 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000769 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100770
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100771 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100772 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100773 struct dhcp_netid_list *list;
774
775 for (list = config->netid; list; list = list->next)
776 {
777 list->list->next = netid;
778 netid = list->list;
779 }
Simon Kelleya2226412004-05-13 20:27:08 +0100780 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100781
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100782 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100783
Simon Kelley26128d22004-11-14 16:43:54 +0000784 /* if all the netids in the ignore list are present, ignore this client */
785 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100786 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000787 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100788
789 /* If configured, we can override the server-id to be the address of the relay,
790 so that all traffic goes via the relay and can pick up agent-id info. This can be
791 configured for all relays, or by address. */
792 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
793 {
794 if (!daemon->override_relays)
795 override = mess->giaddr;
796 else
797 {
798 struct addr_list *l;
799 for (l = daemon->override_relays; l; l = l->next)
800 if (l->addr.s_addr == mess->giaddr.s_addr)
801 break;
802 if (l)
803 override = mess->giaddr;
804 }
805 }
806
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100807 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
808 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000809 clid = NULL;
810
Simon Kelley7622fc02009-06-04 20:32:05 +0100811 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100812 if (daemon->enable_pxe &&
813 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100814 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
815 {
816 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
817 {
818 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
819 uuid = pxe_uuid;
820 }
821
822 /* Check if this is really a PXE bootserver request, and handle specially if so. */
823 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
824 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
825 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
826 {
827 struct pxe_service *service;
828 int type = option_uint(opt, 0, 2);
829 int layer = option_uint(opt, 2, 2);
830 unsigned char save71[4];
831 struct dhcp_opt opt71;
832
Simon Kelley1f15b812009-10-13 17:49:32 +0100833 if (ignore)
834 return 0;
835
Simon Kelley7622fc02009-06-04 20:32:05 +0100836 if (layer & 0x8000)
837 {
838 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
839 return 0;
840 }
841
842 memcpy(save71, option_ptr(opt, 0), 4);
843
844 for (service = daemon->pxe_services; service; service = service->next)
845 if (service->type == type)
846 break;
847
Simon Kelley549b1a42015-05-20 20:20:24 +0100848 for (; context; context = context->current)
849 if (match_netid(context->filter, tagif_netid, 1) &&
850 is_same_net(mess->ciaddr, context->start, context->netmask))
851 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100852
Simon Kelley549b1a42015-05-20 20:20:24 +0100853 if (!service || !service->basename || !context)
854 return 0;
855
Simon Kelleya9df0e32017-04-28 22:43:00 +0100856 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100857
858 mess->yiaddr = mess->ciaddr;
859 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000860 if (service->sname)
861 mess->siaddr = a_record_from_hosts(service->sname, now);
862 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100863 mess->siaddr = service->server;
864 else
865 mess->siaddr = context->local;
866
Matthias Andreef77700a2017-05-21 22:36:09 +0100867 if (strchr(service->basename, '.'))
868 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100869 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100870 else
871 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100872 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100873
Simon Kelley7622fc02009-06-04 20:32:05 +0100874 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
875 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
876 pxe_misc(mess, end, uuid);
877
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100878 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100879 opt71.val = save71;
880 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
881 opt71.len = 4;
882 opt71.flags = DHOPT_VENDOR_MATCH;
883 opt71.netid = NULL;
884 opt71.next = daemon->dhcp_opts;
885 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
886
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100887 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000888 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100889 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100890 }
891
892 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
893 {
894 pxearch = option_uint(opt, 0, 2);
895
Simon Kelley316e2732010-01-22 20:16:09 +0000896 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000897 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100898 {
Simon Kelley28866e92011-02-14 20:19:14 +0000899 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100900 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100901
Simon Kelley28866e92011-02-14 20:19:14 +0000902 for (tmp = context; tmp; tmp = tmp->current)
903 if ((tmp->flags & CONTEXT_PROXY) &&
904 match_netid(tmp->filter, tagif_netid, 1))
905 break;
906
907 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100908 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100909 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100910 int redirect4011 = 0;
911
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100912 if (tmp->netid.net)
913 {
914 tmp->netid.next = netid;
915 tagif_netid = run_tag_if(&tmp->netid);
916 }
917
918 boot = find_boot(tagif_netid);
919
Simon Kelley28866e92011-02-14 20:19:14 +0000920 mess->yiaddr.s_addr = 0;
921 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
922 {
923 mess->ciaddr.s_addr = 0;
924 mess->flags |= htons(0x8000); /* broadcast */
925 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100926
Simon Kelleya9df0e32017-04-28 22:43:00 +0100927 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000928
Simon Kelley0a4a0492016-05-15 20:13:45 +0100929 /* Redirect EFI clients to port 4011 */
930 if (pxearch >= 6)
931 {
932 redirect4011 = 1;
933 mess->siaddr = tmp->local;
934 }
935
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100936 /* Returns true if only one matching service is available. On port 4011,
937 it also inserts the boot file and server name. */
938 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100939
940 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000941 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000942 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100943 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100944 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000945 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100946 else if (boot->tftp_sname)
947 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000948
949 if (boot->file)
Petr Menšík47b45b22018-08-15 18:17:00 +0200950 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley28866e92011-02-14 20:19:14 +0000951 }
952
953 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
954 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100955 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Simon Kelley28866e92011-02-14 20:19:14 +0000956 pxe_misc(mess, end, uuid);
957 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +0100958 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +0100959 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
960
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100961 daemon->metrics[METRIC_PXE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100962 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000963 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +0100964 if (!ignore)
965 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100966 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100967 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100968 }
969 }
970 }
971
972 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000973 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100974 return 0;
975
Simon Kelleybb01cb92004-12-13 20:56:23 +0000976 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100977 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100978 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100979 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000980 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100981 }
982
Simon Kelley3be34542004-09-11 19:12:13 +0100983 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000984 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000985 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000986 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000987 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000988 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100989
Simon Kelley44a2a312004-03-10 20:04:35 +0000990 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100991 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000992
Simon Kelleybb01cb92004-12-13 20:56:23 +0000993 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000994 return 0;
995
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100996 daemon->metrics[METRIC_DHCPDECLINE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100997 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 +0000998
999 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
1000 lease_prune(lease, now);
1001
Simon Kelley33820b72004-04-03 21:10:00 +01001002 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +00001003 config->addr.s_addr == option_addr(opt).s_addr)
1004 {
Simon Kelley849a8352006-06-09 21:02:31 +01001005 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +01001006 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001007 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +01001008 config->flags |= CONFIG_DECLINED;
1009 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +00001010 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001011 else
1012 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +01001013 for (; context; context = context->current)
1014 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +00001015
1016 return 0;
1017
1018 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001019 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +01001020 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001021 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001022 return 0;
1023
Simon Kelley44a2a312004-03-10 20:04:35 +00001024 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
1025 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001026 else
Simon Kelleyb8187c82005-11-26 21:46:27 +00001027 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001028
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001029 daemon->metrics[METRIC_DHCPRELEASE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001030 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001031
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001032 return 0;
1033
1034 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001035 if (ignore || have_config(config, CONFIG_DISABLE))
1036 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001037 if (option_bool(OPT_QUIET_DHCP))
1038 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001039 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001040 opt = NULL;
1041 }
1042 else
1043 {
1044 struct in_addr addr, conf;
1045
Simon Kelley1a6bca82008-07-11 11:11:42 +01001046 addr.s_addr = conf.s_addr = 0;
1047
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001048 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1049 addr = option_addr(opt);
1050
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001051 if (have_config(config, CONFIG_ADDR))
1052 {
Simon Kelley849a8352006-06-09 21:02:31 +01001053 char *addrs = inet_ntoa(config->addr);
1054
Simon Kelley9009d742008-11-14 20:04:27 +00001055 if ((ltmp = lease_find_by_addr(config->addr)) &&
1056 ltmp != lease &&
1057 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001058 {
1059 int len;
1060 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1061 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001062 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +01001063 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001064 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001065 else
1066 {
1067 struct dhcp_context *tmp;
1068 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001069 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001070 break;
1071 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001072 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 +01001073 else if (have_config(config, CONFIG_DECLINED) &&
1074 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001075 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001076 else
1077 conf = config->addr;
1078 }
1079 }
1080
1081 if (conf.s_addr)
1082 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001083 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001084 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001085 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001086 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001087 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001088 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001089 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001090 else if (emac_len == 0)
1091 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001092 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001093 message = _("no address available");
1094 }
1095
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001096 daemon->metrics[METRIC_DHCPDISCOVER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001097 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 +01001098
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001099 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001100 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001101
Simon Kelleycdeda282006-03-16 20:16:06 +00001102 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001103 {
1104 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001105 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001106 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001107
Simon Kelley4cb1b322012-02-06 14:30:41 +00001108 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001109 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001110
1111 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1112 {
1113 rapid_commit = 1;
1114 goto rapid_commit;
1115 }
1116
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001117 daemon->metrics[METRIC_DHCPOFFER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001118 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001119
Simon Kelley824af852008-02-12 20:43:05 +00001120 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001121 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001122 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001123 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001124 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001125 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001126 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001127 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001128
Simon Kelley7de060b2011-08-26 17:24:52 +01001129 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001130
1131
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001132 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001133 if (ignore || have_config(config, CONFIG_DISABLE))
1134 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001135 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001136 {
1137 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001138 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001139
Simon Kelley4011c4e2006-10-28 16:26:19 +01001140 /* send vendor and user class info for new or recreated lease */
1141 do_classes = 1;
1142
Simon Kelleybb01cb92004-12-13 20:56:23 +00001143 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001144 {
Simon Kelley3be34542004-09-11 19:12:13 +01001145 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001146 selecting = 1;
1147
Simon Kelley1a6bca82008-07-11 11:11:42 +01001148 if (override.s_addr != 0)
1149 {
1150 if (option_addr(opt).s_addr != override.s_addr)
1151 return 0;
1152 }
Simon Kelley9009d742008-11-14 20:04:27 +00001153 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001154 {
1155 for (; context; context = context->current)
1156 if (context->local.s_addr == option_addr(opt).s_addr)
1157 break;
1158
1159 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001160 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001161 /* Handle very strange configs where clients have more than one route to the server.
1162 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1163 Have to set override to make sure we echo back the correct server-id */
1164 struct irec *intr;
1165
Simon Kelley115ac3e2013-05-20 11:28:32 +01001166 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001167
1168 for (intr = daemon->interfaces; intr; intr = intr->next)
1169 if (intr->addr.sa.sa_family == AF_INET &&
1170 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1171 intr->tftp_ok)
1172 break;
1173
1174 if (intr)
1175 override = intr->addr.in.sin_addr;
1176 else
1177 {
1178 /* In auth mode, a REQUEST sent to the wrong server
1179 should be faulted, so that the client establishes
1180 communication with us, otherwise, silently ignore. */
1181 if (!option_bool(OPT_AUTHORITATIVE))
1182 return 0;
1183 message = _("wrong server-ID");
1184 }
Simon Kelley9009d742008-11-14 20:04:27 +00001185 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001186 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001187
Simon Kelley3be34542004-09-11 19:12:13 +01001188 /* If a lease exists for this host and another address, squash it. */
1189 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1190 {
1191 lease_prune(lease, now);
1192 lease = NULL;
1193 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001194 }
Simon Kelley3be34542004-09-11 19:12:13 +01001195 else
1196 {
1197 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001198 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001199 return 0;
1200
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001201 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001202 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001203 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001204 }
1205 else
1206 {
1207 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001208 /* Check existing lease for this address.
1209 We allow it to be missing if dhcp-authoritative mode
1210 as long as we can allocate the lease now - checked below.
1211 This makes for a smooth recovery from a lost lease DB */
1212 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001213 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001214 {
Simon Kelley28866e92011-02-14 20:19:14 +00001215 /* A client rebinding will broadcast the request, so we may see it even
1216 if the lease is held by another server. Just ignore it in that case.
1217 If the request is unicast to us, then somethings wrong, NAK */
1218 if (!unicast_dest)
1219 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001220 message = _("lease not found");
1221 /* ensure we broadcast NAK */
1222 unicast_dest = 0;
1223 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001224
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001225 /* desynchronise renewals */
1226 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001227 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001228 }
Simon Kelley734d5312018-03-23 23:09:53 +00001229
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001230 daemon->metrics[METRIC_DHCPREQUEST]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001231 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001232
1233 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001234 if (!message)
1235 {
1236 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001237 struct dhcp_context *tmp = NULL;
1238
1239 if (have_config(config, CONFIG_ADDR))
1240 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001241 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001242 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001243
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001244 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001245 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001246 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001247 message = _("wrong network");
1248 /* ensure we broadcast NAK */
1249 unicast_dest = 0;
1250 }
1251
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001252 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001253 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001254 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001255 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001256
Simon Kelleydfa666f2004-08-02 18:27:27 +01001257 /* Check if a new static address has been configured. Be very sure that
1258 when the client does DISCOVER, it will get the static address, otherwise
1259 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001260 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001261 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001262 (!have_config(config, CONFIG_DECLINED) ||
1263 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001264 config->addr.s_addr != mess->yiaddr.s_addr &&
1265 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001266 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001267
1268 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001269 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001270 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001271
Simon Kelley9009d742008-11-14 20:04:27 +00001272 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1273 {
1274 /* If a host is configured with more than one MAC address, it's OK to 'nix
1275 a lease from one of it's MACs to give the address to another. */
1276 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1277 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001278 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001279 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1280 inet_ntoa(ltmp->addr));
1281 lease = ltmp;
1282 }
Simon Kelley16972692006-10-16 20:04:18 +01001283 else
Simon Kelley9009d742008-11-14 20:04:27 +00001284 message = _("address in use");
1285 }
1286
1287 if (!message)
1288 {
1289 if (emac_len == 0)
1290 message = _("no unique-id");
1291
1292 else if (!lease)
1293 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001294 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001295 do_classes = 1;
1296 else
1297 message = _("no leases left");
1298 }
Simon Kelley16972692006-10-16 20:04:18 +01001299 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001300 }
Simon Kelley16972692006-10-16 20:04:18 +01001301
Simon Kelley44a2a312004-03-10 20:04:35 +00001302 if (message)
1303 {
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001304 daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
Simon Kelley734d5312018-03-23 23:09:53 +00001305 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1306
1307 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1308 if (rapid_commit)
1309 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001310
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001311 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001312 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001313 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001314 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001315 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001316 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1317 a distant subnet which unicast a REQ to us won't work. */
1318 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1319 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1320 {
1321 mess->flags |= htons(0x8000); /* broadcast */
1322 mess->ciaddr.s_addr = 0;
1323 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001324 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001325 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001326 {
Simon Kelley316e2732010-01-22 20:16:09 +00001327 if (context->netid.net)
1328 {
1329 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001330 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001331 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001332
Simon Kelley4cb1b322012-02-06 14:30:41 +00001333 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001334
Simon Kelleydcffad22012-04-24 15:25:18 +01001335 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001336 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001337 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001338 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001339
Simon Kelleydcffad22012-04-24 15:25:18 +01001340#ifdef HAVE_SCRIPT
1341 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001342 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001343 struct dhcp_netid *n;
1344
1345 if (mess->giaddr.s_addr)
1346 lease->giaddr = mess->giaddr;
1347
1348 free(lease->extradata);
1349 lease->extradata = NULL;
1350 lease->extradata_size = lease->extradata_len = 0;
1351
1352 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1353 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1354 add_extradata_opt(lease, oui);
1355 add_extradata_opt(lease, serial);
1356 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001357
1358 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1359 {
1360 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1361 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1362 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1363 }
1364 else
1365 {
1366 add_extradata_opt(lease, NULL);
1367 add_extradata_opt(lease, NULL);
1368 add_extradata_opt(lease, NULL);
1369 }
1370
ZHAO Yuf89cae32016-12-22 22:32:31 +00001371 /* DNSMASQ_REQUESTED_OPTIONS */
1372 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1373 {
1374 int len = option_len(opt);
1375 unsigned char *rop = option_ptr(opt, 0);
1376 char *q = daemon->namebuff;
1377 int i;
1378 for (i = 0; i < len; i++)
1379 {
1380 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1381 }
1382 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1383 }
1384 else
1385 {
1386 add_extradata_opt(lease, NULL);
1387 }
1388
Simon Kelleydcffad22012-04-24 15:25:18 +01001389 /* space-concat tag set */
1390 if (!tagif_netid)
1391 add_extradata_opt(lease, NULL);
1392 else
1393 for (n = tagif_netid; n; n = n->next)
1394 {
1395 struct dhcp_netid *n1;
1396 /* kill dupes */
1397 for (n1 = n->next; n1; n1 = n1->next)
1398 if (strcmp(n->net, n1->net) == 0)
1399 break;
1400 if (!n1)
1401 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1402 }
1403
1404 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1405 {
1406 int len = option_len(opt);
1407 unsigned char *ucp = option_ptr(opt, 0);
1408 /* If the user-class option started as counted strings, the first byte will be zero. */
1409 if (len != 0 && ucp[0] == 0)
1410 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001411 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001412 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001413 }
Simon Kelley316e2732010-01-22 20:16:09 +00001414#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001415 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001416
1417 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1418 {
1419 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001420 hostname = client_hostname;
1421 hostname_auth = 1;
1422 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001423
Simon Kelley824af852008-02-12 20:43:05 +00001424 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001425 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001426
Simon Kelley832af0b2007-01-21 20:01:28 +00001427 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1428 if (!hostname_auth)
1429 {
1430 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001431 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001432 break;
1433 if (id_list)
1434 hostname = NULL;
1435 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001436
1437 /* Last ditch, if configured, generate hostname from mac address */
1438 if (!hostname && emac_len != 0)
1439 {
1440 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1441 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1442 break;
1443 if (id_list)
1444 {
1445 int i;
1446
1447 hostname = daemon->dhcp_buff;
1448 /* buffer is 256 bytes, 3 bytes per octet */
1449 for (i = 0; (i < emac_len) && (i < 80); i++)
1450 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1451 hostname = daemon->dhcp_buff;
1452 }
1453 }
1454
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001455 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001456 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001457
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001458 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001459 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001460
1461 if (override.s_addr != 0)
1462 lease->override = override;
1463 else
1464 override = lease->override;
1465
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001466 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001467 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001468
Simon Kelleya9df0e32017-04-28 22:43:00 +01001469 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001470 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001471 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001472 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001473 if (rapid_commit)
1474 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1475 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001476 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001477 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001478
Simon Kelley7de060b2011-08-26 17:24:52 +01001479 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001480
1481 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001482 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001483 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001484
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001485 daemon->metrics[METRIC_DHCPINFORM]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001486 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001487
Simon Kelley73a08a22009-02-05 20:28:08 +00001488 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001489 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001490
1491 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001492 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001493
Simon Kelley5aabfc72007-08-29 11:24:47 +01001494 /* Find a least based on IP address if we didn't
1495 get one from MAC address/client-d */
1496 if (!lease &&
1497 (lease = lease_find_by_addr(mess->ciaddr)) &&
1498 lease->hostname)
1499 hostname = lease->hostname;
1500
Simon Kelley0f371f92013-07-27 15:15:38 +01001501 if (!hostname)
1502 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001503
Simon Kelley73a08a22009-02-05 20:28:08 +00001504 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001505 {
1506 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001507 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001508 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001509
Simon Kelley4cb1b322012-02-06 14:30:41 +00001510 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001511
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001512 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001513 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001514
Simon Kelley3927da42008-07-20 15:10:39 +01001515 if (lease)
1516 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001517 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001518 if (override.s_addr != 0)
1519 lease->override = override;
1520 else
1521 override = lease->override;
1522 }
1523
Simon Kelleya9df0e32017-04-28 22:43:00 +01001524 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001525 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001526 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001527
1528 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1529 we supply a utility which makes DHCPINFORM requests to get this information.
1530 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1531 which won't be true for ordinary clients, but will be true for the
1532 dhcp_lease_time utility. */
1533 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1534 {
1535 if (lease->expires == 0)
1536 time = 0xffffffff;
1537 else
1538 time = (unsigned int)difftime(lease->expires, now);
1539 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1540 }
1541
Simon Kelley9009d742008-11-14 20:04:27 +00001542 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001543 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001544
Simon Kelley5aabfc72007-08-29 11:24:47 +01001545 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001546 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001547 }
1548
1549 return 0;
1550}
1551
Simon Kelley6b010842007-02-12 20:32:07 +00001552/* find a good value to use as MAC address for logging and address-allocation hashing.
1553 This is normally just the chaddr field from the DHCP packet,
1554 but eg Firewire will have hlen == 0 and use the client-id instead.
1555 This could be anything, but will normally be EUI64 for Firewire.
1556 We assume that if the first byte of the client-id equals the htype byte
1557 then the client-id is using the usual encoding and use the rest of the
1558 client-id: if not we can use the whole client-id. This should give
1559 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001560unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001561 int clid_len, unsigned char *clid, int *len_out)
1562{
1563 if (hwlen == 0 && clid && clid_len > 3)
1564 {
1565 if (clid[0] == hwtype)
1566 {
1567 *len_out = clid_len - 1 ;
1568 return clid + 1;
1569 }
1570
1571#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1572 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1573 {
1574 *len_out = clid_len - 1 ;
1575 return clid + 1;
1576 }
1577#endif
1578
1579 *len_out = clid_len;
1580 return clid;
1581 }
1582
1583 *len_out = hwlen;
1584 return hwaddr;
1585}
1586
Simon Kelley824af852008-02-12 20:43:05 +00001587static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001588{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001589 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001590
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001591 if (opt)
1592 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001593 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001594 if (req_time < 120 )
1595 req_time = 120; /* sanity */
1596 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1597 time = req_time;
1598 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001599
1600 return time;
1601}
1602
Simon Kelley73a08a22009-02-05 20:28:08 +00001603static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001604{
Simon Kelley73a08a22009-02-05 20:28:08 +00001605 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001606 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001607 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001608 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001609 else
1610 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001611}
1612
Simon Kelleyf2621c72007-04-29 19:47:21 +01001613static int sanitise(unsigned char *opt, char *buf)
1614{
1615 char *p;
1616 int i;
1617
1618 *buf = 0;
1619
1620 if (!opt)
1621 return 0;
1622
Simon Kelley1a6bca82008-07-11 11:11:42 +01001623 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001624
1625 for (i = option_len(opt); i > 0; i--)
1626 {
1627 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001628 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001629 *buf++ = c;
1630 }
1631 *buf = 0; /* add terminator */
1632
1633 return 1;
1634}
1635
Simon Kelley316e2732010-01-22 20:16:09 +00001636#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001637static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1638{
1639 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001640 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001641 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001642 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001643}
1644#endif
1645
Simon Kelley5aabfc72007-08-29 11:24:47 +01001646static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001647 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001648{
Simon Kelley16972692006-10-16 20:04:18 +01001649 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001650
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001651 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001652 return;
1653
Simon Kelley16972692006-10-16 20:04:18 +01001654 /* addr may be misaligned */
1655 if (addr)
1656 memcpy(&a, addr, sizeof(a));
1657
Simon Kelley7622fc02009-06-04 20:32:05 +01001658 print_mac(daemon->namebuff, ext_mac, mac_len);
1659
Simon Kelley28866e92011-02-14 20:19:14 +00001660 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001661 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001662 ntohl(xid),
1663 type,
1664 interface,
1665 addr ? inet_ntoa(a) : "",
1666 addr ? " " : "",
1667 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001668 string ? string : "",
1669 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001670 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001671 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001672 type,
1673 interface,
1674 addr ? inet_ntoa(a) : "",
1675 addr ? " " : "",
1676 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001677 string ? string : "",
1678 err ? err : "");
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001679
1680#ifdef HAVE_UBUS
1681 if (!strcmp(type, "DHCPACK"))
1682 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1683 else if (!strcmp(type, "DHCPRELEASE"))
1684 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1685#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001686}
1687
Simon Kelley7622fc02009-06-04 20:32:05 +01001688static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001689{
1690 while (*start != OPTION_END)
1691 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001692 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 +01001693
Simon Kelley4cb1b322012-02-06 14:30:41 +00001694 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1695 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001696 start += start[1] + 2;
1697 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001698}
1699
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001700static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001701{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001702 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001703 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001704 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001705 return NULL;
1706 else if (*p == OPTION_END)
1707 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001708 else if (*p == OPTION_PAD)
1709 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001710 else
1711 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001712 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001713 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001714 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001715 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001716 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001717 return NULL; /* malformed packet */
1718 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001719 return p;
1720 p += opt_len + 2;
1721 }
1722 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001723}
1724
Simon Kelleycdeda282006-03-16 20:16:06 +00001725static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001726{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001727 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001728
Simon Kelley3be34542004-09-11 19:12:13 +01001729 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001730 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1731 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001732
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001733 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001734 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001735 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001736
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001737 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001738 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001739 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1740 return ret;
1741
1742 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001743 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001744 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1745 return ret;
1746
1747 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001748}
1749
Simon Kelley4cb1b322012-02-06 14:30:41 +00001750static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001751{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001752 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001753 /* struct in_addr is network byte order */
1754 struct in_addr ret;
1755
Simon Kelley4cb1b322012-02-06 14:30:41 +00001756 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001757
1758 return ret;
1759}
1760
Simon Kelley7622fc02009-06-04 20:32:05 +01001761static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001762{
1763 /* this worries about unaligned data and byte order */
1764 unsigned int ret = 0;
1765 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001766 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001767
1768 for (i = 0; i < size; i++)
1769 ret = (ret << 8) | *p++;
1770
1771 return ret;
1772}
1773
1774static unsigned char *dhcp_skip_opts(unsigned char *start)
1775{
1776 while (*start != 0)
1777 start += start[1] + 2;
1778 return start;
1779}
1780
1781/* only for use when building packet: doesn't check for bad data. */
1782static unsigned char *find_overload(struct dhcp_packet *mess)
1783{
1784 unsigned char *p = &mess->options[0] + sizeof(u32);
1785
1786 while (*p != 0)
1787 {
1788 if (*p == OPTION_OVERLOAD)
1789 return p;
1790 p += p[1] + 2;
1791 }
1792 return NULL;
1793}
1794
Simon Kelley7de060b2011-08-26 17:24:52 +01001795static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1796{
1797 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1798 unsigned char *overload;
1799 size_t ret;
1800
1801 /* move agent_id back down to the end of the packet */
1802 if (agent_id)
1803 {
1804 memmove(p, agent_id, real_end - agent_id);
1805 p += real_end - agent_id;
1806 memset(p, 0, real_end - p); /* in case of overlap */
1807 }
1808
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001809 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001810 overload = find_overload(mess);
1811
1812 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001813 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001814 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001815 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001816 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001817 }
Simon Kelley28866e92011-02-14 20:19:14 +00001818 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001819 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1820
1821 if (overload && (option_uint(overload, 0, 1) & 2))
1822 {
1823 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001824 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001825 log_options(mess->sname, mess->xid);
1826 }
Simon Kelley28866e92011-02-14 20:19:14 +00001827 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001828 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1829
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001830
1831 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001832
Simon Kelley28866e92011-02-14 20:19:14 +00001833 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001834 {
1835 if (mess->siaddr.s_addr != 0)
1836 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1837
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001838 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1839 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1840
Simon Kelley7622fc02009-06-04 20:32:05 +01001841 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1842 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001843
1844 ret = (size_t)(p - (unsigned char *)mess);
1845
1846 if (ret < MIN_PACKETSZ)
1847 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001848
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001849 return ret;
1850}
1851
1852static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1853{
1854 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1855
1856 if (p + len + 3 >= end)
1857 /* not enough space in options area, try and use overload, if poss */
1858 {
1859 unsigned char *overload;
1860
1861 if (!(overload = find_overload(mess)) &&
1862 (mess->file[0] == 0 || mess->sname[0] == 0))
1863 {
1864 /* attempt to overload fname and sname areas, we've reserved space for the
1865 overflow option previuously. */
1866 overload = p;
1867 *(p++) = OPTION_OVERLOAD;
1868 *(p++) = 1;
1869 }
1870
1871 p = NULL;
1872
1873 /* using filename field ? */
1874 if (overload)
1875 {
1876 if (mess->file[0] == 0)
1877 overload[2] |= 1;
1878
1879 if (overload[2] & 1)
1880 {
1881 p = dhcp_skip_opts(mess->file);
1882 if (p + len + 3 >= mess->file + sizeof(mess->file))
1883 p = NULL;
1884 }
1885
1886 if (!p)
1887 {
1888 /* try to bring sname into play (it may be already) */
1889 if (mess->sname[0] == 0)
1890 overload[2] |= 2;
1891
1892 if (overload[2] & 2)
1893 {
1894 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001895 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001896 p = NULL;
1897 }
1898 }
1899 }
1900
1901 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001902 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 +00001903 }
1904
1905 if (p)
1906 {
1907 *(p++) = opt;
1908 *(p++) = len;
1909 }
1910
1911 return p;
1912}
1913
1914static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1915{
1916 int i;
1917 unsigned char *p = free_space(mess, end, opt, len);
1918
1919 if (p)
1920 for (i = 0; i < len; i++)
1921 *(p++) = val >> (8 * (len - (i + 1)));
1922}
1923
1924static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1925 char *string, int null_term)
1926{
1927 unsigned char *p;
1928 size_t len = strlen(string);
1929
1930 if (null_term && len != 255)
1931 len++;
1932
1933 if ((p = free_space(mess, end, opt, len)))
1934 memcpy(p, string, len);
1935}
1936
1937/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001938static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001939{
1940 int len = opt->len;
1941
1942 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1943 len++;
1944
1945 if (p && len != 0)
1946 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001947 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001948 {
1949 int j;
1950 struct in_addr *a = (struct in_addr *)opt->val;
1951 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1952 {
1953 /* zero means "self" (but not in vendorclass options.) */
1954 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001955 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001956 else
1957 memcpy(p, a, INADDRSZ);
1958 p += INADDRSZ;
1959 }
1960 }
1961 else
Simon Kelley625ac282013-07-02 21:19:32 +01001962 /* empty string may be extended to "\0" by null_term */
1963 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001964 }
1965 return len;
1966}
Simon Kelley7622fc02009-06-04 20:32:05 +01001967
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001968static int in_list(unsigned char *list, int opt)
1969{
1970 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001971
1972 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001973 if (!list)
1974 return 1;
1975
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001976 for (i = 0; list[i] != OPTION_END; i++)
1977 if (opt == list[i])
1978 return 1;
1979
1980 return 0;
1981}
1982
Simon Kelley7de060b2011-08-26 17:24:52 +01001983static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001984{
Simon Kelley7de060b2011-08-26 17:24:52 +01001985 struct dhcp_opt *opts;
1986
1987 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1988 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1989 return opts;
1990
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001991 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001992}
1993
Simon Kelley6b010842007-02-12 20:32:07 +00001994/* mark vendor-encapsulated options which match the client-supplied or
1995 config-supplied vendor class */
1996static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1997{
1998 for (; dopt; dopt = dopt->next)
1999 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002000 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00002001 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00002002 {
2003 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00002004 if (dopt->u.vendor_class)
2005 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00002006 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00002007 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00002008 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002009 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00002010 break;
2011 }
2012 }
2013 }
2014}
2015
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002016static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
2017 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00002018{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002019 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01002020 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00002021 unsigned char *p;
2022
2023 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01002024 for (enc_len = 0, start = opt; opt; opt = opt->next)
2025 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002026 {
2027 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002028 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00002029 if (enc_len + new <= 255)
2030 enc_len += new;
2031 else
2032 {
2033 p = free_space(mess, end, encap, enc_len);
2034 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002035 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00002036 {
2037 len = do_opt(start, p + 2, NULL, null_term);
2038 *(p++) = start->opt;
2039 *(p++) = len;
2040 p += len;
2041 }
2042 enc_len = new;
2043 start = opt;
2044 }
2045 }
2046
2047 if (enc_len != 0 &&
2048 (p = free_space(mess, end, encap, enc_len + 1)))
2049 {
2050 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002051 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002052 {
2053 len = do_opt(start, p + 2, NULL, null_term);
2054 *(p++) = start->opt;
2055 *(p++) = len;
2056 p += len;
2057 }
2058 *p = OPTION_END;
2059 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002060
2061 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002062}
2063
Simon Kelley7622fc02009-06-04 20:32:05 +01002064static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002065{
Simon Kelley7622fc02009-06-04 20:32:05 +01002066 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002067
Simon Kelley7622fc02009-06-04 20:32:05 +01002068 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2069 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2070 memcpy(p, uuid, 17);
2071}
2072
2073static int prune_vendor_opts(struct dhcp_netid *netid)
2074{
2075 int force = 0;
2076 struct dhcp_opt *opt;
2077
2078 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2079 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2080 if (opt->flags & DHOPT_VENDOR_MATCH)
2081 {
2082 if (!match_netid(opt->netid, netid, 1))
2083 opt->flags &= ~DHOPT_VENDOR_MATCH;
2084 else if (opt->flags & DHOPT_FORCE)
2085 force = 1;
2086 }
2087 return force;
2088}
2089
Simon Kelley8628cd62016-05-10 17:31:48 +01002090
2091/* Many UEFI PXE implementations have badly broken menu code.
2092 If there's exactly one relevant menu item, we abandon the menu system,
2093 and jamb the data direct into the DHCP file, siaddr and sname fields.
2094 Note that in this case, we have to assume that layer zero would be requested
2095 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002096static 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 +01002097{
2098 struct pxe_service *service, *found;
2099
2100 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002101 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002102 return 0;
2103
2104 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002105 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002106 {
2107 if (found)
2108 return 0; /* More than one relevant menu item */
2109
2110 found = service;
2111 }
2112
2113 if (!found)
2114 return 0; /* No relevant menu items. */
2115
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002116 if (!pxe)
2117 return 1;
2118
Simon Kelley8628cd62016-05-10 17:31:48 +01002119 if (found->sname)
2120 {
2121 mess->siaddr = a_record_from_hosts(found->sname, now);
2122 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2123 }
2124 else
2125 {
2126 if (found->server.s_addr != 0)
2127 mess->siaddr = found->server;
2128 else
2129 mess->siaddr = local;
2130
2131 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2132 }
2133
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002134 snprintf((char *)mess->file, sizeof(mess->file),
2135 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2136
Simon Kelley8628cd62016-05-10 17:31:48 +01002137 return 1;
2138}
2139
Simon Kelley751d6f42012-02-10 15:24:51 +00002140static 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 +01002141{
2142#define NUM_OPTS 4
2143
2144 unsigned char *p, *q;
2145 struct pxe_service *service;
2146 static struct dhcp_opt *o, *ret;
2147 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002148 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002149
2150 /* We pass back references to these, hence they are declared static */
2151 static unsigned char discovery_control;
2152 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2153 static struct dhcp_opt *fake_opts = NULL;
2154
Simon Kelley316e2732010-01-22 20:16:09 +00002155 /* Disable multicast, since we don't support it, and broadcast
2156 unless we need it */
2157 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002158
2159 ret = daemon->dhcp_opts;
2160
2161 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2162 return ret;
2163
2164 for (i = 0; i < NUM_OPTS; i++)
2165 {
2166 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2167 fake_opts[i].netid = NULL;
2168 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2169 }
2170
2171 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2172 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002173 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002174
2175 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2176 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2177 {
2178 size_t len = strlen(service->menu);
2179 /* opt 43 max size is 255. encapsulated option has type and length
2180 bytes, so its max size is 253. */
2181 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2182 {
2183 *(p++) = service->type >> 8;
2184 *(p++) = service->type;
2185 *(p++) = len;
2186 memcpy(p, service->menu, len);
2187 p += len;
2188 i++;
2189 }
2190 else
2191 {
2192 toobig:
2193 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2194 return daemon->dhcp_opts;
2195 }
2196
Simon Kelley751d6f42012-02-10 15:24:51 +00002197 boot_server = service->basename ? local :
2198 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2199
Simon Kelley316e2732010-01-22 20:16:09 +00002200 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002201 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002202 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002203 goto toobig;
2204
2205 /* Boot service with known address - give it */
2206 *(q++) = service->type >> 8;
2207 *(q++) = service->type;
2208 *(q++) = 1;
2209 /* dest misaligned */
2210 memcpy(q, &boot_server.s_addr, INADDRSZ);
2211 q += INADDRSZ;
2212 }
2213 else if (service->type != 0)
2214 /* We don't know the server for a service type, so we'll
2215 allow the client to broadcast for it */
2216 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002217 }
2218
2219 /* if no prompt, wait forever if there's a choice */
2220 fake_prompt[0] = (i > 1) ? 255 : 0;
2221
2222 if (i == 0)
2223 discovery_control = 8; /* no menu - just use use mess->filename */
2224 else
2225 {
2226 ret = &fake_opts[j--];
2227 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2228 ret->val = (unsigned char *)daemon->dhcp_buff;
2229 ret->opt = SUBOPT_PXE_MENU;
2230
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002231 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002232 {
2233 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002234 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2235 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002236 ret->opt = SUBOPT_PXE_SERVERS;
2237 }
2238 }
2239
2240 for (o = daemon->dhcp_opts; o; o = o->next)
2241 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2242 break;
2243
2244 if (!o)
2245 {
2246 ret = &fake_opts[j--];
2247 ret->len = sizeof(fake_prompt);
2248 ret->val = fake_prompt;
2249 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2250 }
2251
Simon Kelley316e2732010-01-22 20:16:09 +00002252 ret = &fake_opts[j--];
2253 ret->len = 1;
2254 ret->opt = SUBOPT_PXE_DISCOVERY;
2255 ret->val= &discovery_control;
2256
Simon Kelley7622fc02009-06-04 20:32:05 +01002257 return ret;
2258}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002259
2260static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002261{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002262 memset(mess->sname, 0, sizeof(mess->sname));
2263 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002264 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002265 mess->siaddr.s_addr = 0;
2266}
Simon Kelleycdeda282006-03-16 20:16:06 +00002267
Simon Kelley7622fc02009-06-04 20:32:05 +01002268struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002269{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002270 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002271
2272 /* decide which dhcp-boot option we're using */
2273 for (boot = daemon->boot_config; boot; boot = boot->next)
2274 if (match_netid(boot->netid, netid, 0))
2275 break;
2276 if (!boot)
2277 /* No match, look for one without a netid */
2278 for (boot = daemon->boot_config; boot; boot = boot->next)
2279 if (match_netid(boot->netid, netid, 1))
2280 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002281
2282 return boot;
2283}
2284
2285static void do_options(struct dhcp_context *context,
2286 struct dhcp_packet *mess,
2287 unsigned char *end,
2288 unsigned char *req_options,
2289 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002290 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002291 struct dhcp_netid *netid,
2292 struct in_addr subnet_addr,
2293 unsigned char fqdn_flags,
2294 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002295 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002296 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002297 time_t now,
2298 unsigned int lease_time,
2299 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002300{
2301 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2302 struct dhcp_boot *boot;
2303 unsigned char *p;
2304 int i, len, force_encap = 0;
2305 unsigned char f0 = 0, s0 = 0;
2306 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002307 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002308 struct dhcp_netid *tagif;
2309 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002310
Simon Kelley4cb1b322012-02-06 14:30:41 +00002311 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002312 if (context)
2313 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002314 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002315
Simon Kelley7622fc02009-06-04 20:32:05 +01002316 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002317 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002318 {
2319 char *q = daemon->namebuff;
2320 for (i = 0; req_options[i] != OPTION_END; i++)
2321 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002322 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002323 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2324 "%d%s%s%s",
2325 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002326 strlen(s) != 0 ? ":" : "",
2327 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002328 req_options[i+1] == OPTION_END ? "" : ", ");
2329 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2330 {
2331 q = daemon->namebuff;
2332 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2333 }
2334 }
2335 }
2336
Simon Kelley7de060b2011-08-26 17:24:52 +01002337 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2338 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2339 break;
2340 if (id_list)
2341 mess->flags |= htons(0x8000); /* force broadcast */
2342
Simon Kelley73a08a22009-02-05 20:28:08 +00002343 if (context)
2344 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002345
2346 /* See if we can send the boot stuff as options.
2347 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002348 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002349 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002350 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002351 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002352 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002353 {
2354 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002355 {
Simon Kelley28866e92011-02-14 20:19:14 +00002356 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002357 req_options &&
2358 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002359 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2360 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002361 safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002362 }
2363
2364 if (boot->file)
2365 {
Simon Kelley28866e92011-02-14 20:19:14 +00002366 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002367 req_options &&
2368 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002369 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2370 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002371 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002372 }
2373
Simon Kelley7de060b2011-08-26 17:24:52 +01002374 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002375 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002376 else if (boot->tftp_sname)
2377 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002378 }
Simon Kelley824af852008-02-12 20:43:05 +00002379 else
2380 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002381 they're not explicitly asked for as options. OPTION_END is used
2382 as an internal way to specify siaddr without using dhcp-boot, for use in
2383 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002384 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002385 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002386 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002387 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002388 safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
Simon Kelley824af852008-02-12 20:43:05 +00002389 done_file = 1;
2390 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002391
Simon Kelley824af852008-02-12 20:43:05 +00002392 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002393 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002394 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002395 safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
Simon Kelley824af852008-02-12 20:43:05 +00002396 done_server = 1;
2397 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002398
Simon Kelley7de060b2011-08-26 17:24:52 +01002399 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002400 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002401 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002402
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002403 /* We don't want to do option-overload for BOOTP, so make the file and sname
2404 fields look like they are in use, even when they aren't. This gets restored
2405 at the end of this function. */
2406
Simon Kelley28866e92011-02-14 20:19:14 +00002407 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002408 {
2409 f0 = mess->file[0];
2410 mess->file[0] = 1;
2411 s0 = mess->sname[0];
2412 mess->sname[0] = 1;
2413 }
2414
2415 /* At this point, if mess->sname or mess->file are zeroed, they are available
2416 for option overload, reserve space for the overload option. */
2417 if (mess->file[0] == 0 || mess->sname[0] == 0)
2418 end -= 3;
2419
Simon Kelley3be34542004-09-11 19:12:13 +01002420 /* rfc3011 says this doesn't need to be in the requested options list. */
2421 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002422 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002423
2424 if (lease_time != 0xffffffff)
2425 {
2426 unsigned int t1val = lease_time/2;
2427 unsigned int t2val = (lease_time*7)/8;
2428 unsigned int hval;
2429
2430 /* If set by user, sanity check, so not longer than lease. */
2431 if ((opt = option_find2(OPTION_T1)))
2432 {
2433 hval = ntohl(*((unsigned int *)opt->val));
2434 if (hval < lease_time && hval > 2)
2435 t1val = hval;
2436 }
2437
2438 if ((opt = option_find2(OPTION_T2)))
2439 {
2440 hval = ntohl(*((unsigned int *)opt->val));
2441 if (hval < lease_time && hval > 2)
2442 t2val = hval;
2443 }
2444
Simon Kelley7c0f2542015-05-14 21:16:18 +01002445 /* ensure T1 is still < T2 */
2446 if (t2val <= t1val)
2447 t1val = t2val - 1;
2448
Simon Kelleyca85a282015-05-13 22:33:04 +01002449 while (fuzz > (t1val/8))
2450 fuzz = fuzz/2;
2451
2452 t1val -= fuzz;
2453 t2val -= fuzz;
2454
Simon Kelleyca85a282015-05-13 22:33:04 +01002455 option_put(mess, end, OPTION_T1, 4, t1val);
2456 option_put(mess, end, OPTION_T2, 4, t2val);
2457 }
2458
Simon Kelley73a08a22009-02-05 20:28:08 +00002459 /* replies to DHCPINFORM may not have a valid context */
2460 if (context)
2461 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002462 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002463 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2464
2465 /* May not have a "guessed" broadcast address if we got no packets via a relay
2466 from this net yet (ie just unicast renewals after a restart */
2467 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002468 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002469 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2470
2471 /* Same comments as broadcast apply, and also may not be able to get a sensible
2472 default when using subnet select. User must configure by steam in that case. */
2473 if (context->router.s_addr &&
2474 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002475 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002476 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2477
Simon Kelleya21e27b2013-02-17 16:41:35 +00002478 if (daemon->port == NAMESERVER_PORT &&
2479 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002480 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002481 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2482 }
Simon Kelley3be34542004-09-11 19:12:13 +01002483
Simon Kelley9009d742008-11-14 20:04:27 +00002484 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002485 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002486 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002487
Simon Kelley824af852008-02-12 20:43:05 +00002488 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002489 if (hostname)
2490 {
Simon Kelley824af852008-02-12 20:43:05 +00002491 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002492 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002493 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002494
2495 if (fqdn_flags != 0)
2496 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002497 len = strlen(hostname) + 3;
2498
Simon Kelley3d8df262005-08-29 12:19:27 +01002499 if (fqdn_flags & 0x04)
2500 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002501 else if (null_term)
2502 len++;
2503
Simon Kelley9009d742008-11-14 20:04:27 +00002504 if (domain)
2505 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002506 else if (fqdn_flags & 0x04)
2507 len--;
2508
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002509 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002510 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002511 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002512 *(p++) = 255;
2513 *(p++) = 255;
2514
2515 if (fqdn_flags & 0x04)
2516 {
Simon Kelley0549c732017-09-25 18:17:11 +01002517 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002518 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002519 {
Simon Kelley0549c732017-09-25 18:17:11 +01002520 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002521 *p++ = 0;
2522 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002523 }
2524 else
2525 {
2526 memcpy(p, hostname, strlen(hostname));
2527 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002528 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002529 {
2530 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002531 memcpy(p, domain, strlen(domain));
2532 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002533 }
2534 if (null_term)
2535 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002536 }
2537 }
2538 }
2539 }
2540
Simon Kelley6b010842007-02-12 20:32:07 +00002541 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002542 {
Simon Kelley824af852008-02-12 20:43:05 +00002543 int optno = opt->opt;
2544
Simon Kelley7de060b2011-08-26 17:24:52 +01002545 /* netids match and not encapsulated? */
2546 if (!(opt->flags & DHOPT_TAGOK))
2547 continue;
2548
Simon Kelley6b010842007-02-12 20:32:07 +00002549 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002550 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002551 continue;
2552
Simon Kelleyca85a282015-05-13 22:33:04 +01002553 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002554 if (optno == OPTION_CLIENT_FQDN ||
2555 optno == OPTION_MAXMESSAGE ||
2556 optno == OPTION_OVERLOAD ||
2557 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002558 optno == OPTION_END ||
2559 optno == OPTION_T1 ||
2560 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002561 continue;
2562
2563 if (optno == OPTION_SNAME && done_server)
2564 continue;
2565
2566 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002567 continue;
2568
Simon Kelley33820b72004-04-03 21:10:00 +01002569 /* For the options we have default values on
2570 dhc-option=<optionno> means "don't include this option"
2571 not "include a zero-length option" */
2572 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002573 (optno == OPTION_NETMASK ||
2574 optno == OPTION_BROADCAST ||
2575 optno == OPTION_ROUTER ||
2576 optno == OPTION_DNSSERVER ||
2577 optno == OPTION_DOMAINNAME ||
2578 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002579 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002580
2581 /* vendor-class comes from elsewhere for PXE */
2582 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2583 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002584
Simon Kelley7622fc02009-06-04 20:32:05 +01002585 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002586 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002587 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002588
Simon Kelley824af852008-02-12 20:43:05 +00002589 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002590 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002591 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002592 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2593
Simon Kelley6b010842007-02-12 20:32:07 +00002594 /* If we send a vendor-id, revisit which vendor-ops we consider
2595 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002596 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002597 {
2598 match_vendor_opts(p - 2, config_opts);
2599 done_vendor_class = 1;
2600 }
Simon Kelley6b010842007-02-12 20:32:07 +00002601 }
2602 }
2603
Simon Kelley73a08a22009-02-05 20:28:08 +00002604 /* Now send options to be encapsulated in arbitrary options,
2605 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002606 Also handle vendor-identifying vendor-encapsulated options,
2607 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002608 The may be more that one "outer" to do, so group
2609 all the options which match each outer in turn. */
2610 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002611 opt->flags &= ~DHOPT_ENCAP_DONE;
2612
2613 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002614 {
2615 int flags;
2616
2617 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2618 {
2619 int found = 0;
2620 struct dhcp_opt *o;
2621
2622 if (opt->flags & DHOPT_ENCAP_DONE)
2623 continue;
2624
2625 for (len = 0, o = config_opts; o; o = o->next)
2626 {
2627 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2628
2629 o->flags &= ~DHOPT_ENCAP_MATCH;
2630
2631 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2632 continue;
2633
2634 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002635 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002636 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2637 {
2638 o->flags |= DHOPT_ENCAP_MATCH;
2639 found = 1;
2640 len += do_opt(o, NULL, NULL, 0) + 2;
2641 }
2642 }
2643
2644 if (found)
2645 {
2646 if (flags & DHOPT_ENCAPSULATE)
2647 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2648 else if (len > 250)
2649 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2650 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2651 {
2652 int swap_ent = htonl(opt->u.encap);
2653 memcpy(p, &swap_ent, 4);
2654 p += 4;
2655 *(p++) = len;
2656 for (o = config_opts; o; o = o->next)
2657 if (o->flags & DHOPT_ENCAP_MATCH)
2658 {
2659 len = do_opt(o, p + 2, NULL, 0);
2660 *(p++) = o->opt;
2661 *(p++) = len;
2662 p += len;
2663 }
2664 }
2665 }
2666 }
2667 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002668
Simon Kelley7de060b2011-08-26 17:24:52 +01002669 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002670
Simon Kelley316e2732010-01-22 20:16:09 +00002671 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002672 {
2673 pxe_misc(mess, end, uuid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002674 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002675 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002676 }
2677
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002678 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2679 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2680 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2681 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2682 /* If we send vendor encapsulated options, and haven't already sent option 60,
2683 echo back the value we got from the client. */
2684 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2685
Simon Kelley7622fc02009-06-04 20:32:05 +01002686 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002687 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002688 {
2689 mess->file[0] = f0;
2690 mess->sname[0] = s0;
2691 }
2692}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002693
Floris Bos503c6092017-04-09 23:07:13 +01002694static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2695{
2696 struct delay_config *delay_conf;
2697
2698 /* Decide which delay_config option we're using */
2699 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2700 if (match_netid(delay_conf->netid, netid, 0))
2701 break;
2702
2703 if (!delay_conf)
2704 /* No match, look for one without a netid */
2705 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2706 if (match_netid(delay_conf->netid, netid, 1))
2707 break;
2708
2709 if (delay_conf)
2710 {
2711 if (!option_bool(OPT_QUIET_DHCP))
2712 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2713 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2714 }
2715}
2716
Simon Kelley7622fc02009-06-04 20:32:05 +01002717#endif
2718
2719
2720
2721
2722
2723
2724