blob: 74d81fb598b098cba74c9df37583d9c761232b7f [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 Kelleyae5b7e02019-03-27 22:33:28 +0000277 struct shared_network *share = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100278 struct in_addr addr;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000279 int force = 0, via_relay = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000280
Simon Kelley3d8df262005-08-29 12:19:27 +0100281 if (subnet_addr.s_addr)
282 {
283 addr = subnet_addr;
284 force = 1;
285 }
286 else if (mess->giaddr.s_addr)
287 {
288 addr = mess->giaddr;
289 force = 1;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000290 via_relay = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100291 }
Simon Kelley16972692006-10-16 20:04:18 +0100292 else
293 {
294 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
295 addr = mess->ciaddr;
296 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
297 if (context_tmp->netmask.s_addr &&
298 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
299 is_same_net(addr, context_tmp->end, context_tmp->netmask))
300 {
301 context_new = context;
302 break;
303 }
304 }
305
306 if (!context_new)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000307 {
308 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
309 {
310 struct in_addr netmask = context_tmp->netmask;
311
312 /* guess the netmask for relayed networks */
313 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
314 {
315 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
316 netmask.s_addr = htonl(0xff000000);
317 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
318 netmask.s_addr = htonl(0xffff0000);
319 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
320 netmask.s_addr = htonl(0xffffff00);
321 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100322
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000323 /* check to see is a context is OK because of a shared address on
324 the relayed subnet. */
325 if (via_relay)
326 for (share = daemon->shared_networks; share; share = share->next)
327 {
328#ifdef HAVE_DHCP6
329 if (share->shared_addr.s_addr == 0)
330 continue;
331#endif
332 if (share->if_index != 0 ||
333 share->match_addr.s_addr != mess->giaddr.s_addr)
334 continue;
335
336 if (netmask.s_addr != 0 &&
337 is_same_net(share->shared_addr, context_tmp->start, netmask) &&
338 is_same_net(share->shared_addr, context_tmp->end, netmask))
339 break;
340 }
341
342 /* This section fills in context mainly when a client which is on a remote (relayed)
343 network renews a lease without using the relay, after dnsmasq has restarted. */
344 if (share ||
345 (netmask.s_addr != 0 &&
346 is_same_net(addr, context_tmp->start, netmask) &&
347 is_same_net(addr, context_tmp->end, netmask)))
348 {
349 context_tmp->netmask = netmask;
350 if (context_tmp->local.s_addr == 0)
351 context_tmp->local = fallback;
352 if (context_tmp->router.s_addr == 0 && !share)
353 context_tmp->router = mess->giaddr;
354
355 /* fill in missing broadcast addresses for relayed ranges */
356 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
357 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
358
359 context_tmp->current = context_new;
360 context_new = context_tmp;
361 }
362
363 }
364 }
365
Simon Kelley3d8df262005-08-29 12:19:27 +0100366 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100367 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000368 }
Simon Kelley3be34542004-09-11 19:12:13 +0100369
370 if (!context)
371 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100372 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100373 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
374 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 +0100375 return 0;
376 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100377
Simon Kelley28866e92011-02-14 20:19:14 +0000378 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100379 {
380 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100381 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
382 {
383 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100384 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100385 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100386 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100387 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100388 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100389 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100390 }
391 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100392
393 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
394 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000395 If no data given, existence of the option is enough. This code handles
Simon Kelley86e92f92013-04-23 11:31:39 +0100396 rfc3925 V-I classes too. */
397 for (o = daemon->dhcp_match; o; o = o->next)
398 {
399 unsigned int len, elen, match = 0;
400 size_t offset, o2;
401
402 if (o->flags & DHOPT_RFC3925)
403 {
404 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
405 continue;
406
407 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
408 {
409 len = option_uint(opt, offset + 4 , 1);
410 /* Need to take care that bad data can't run us off the end of the packet */
Matthias Andree9828ab12017-05-21 22:41:16 +0100411 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100412 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
413 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
414 {
415 elen = option_uint(opt, o2, 1);
416 if ((o2 + elen + 1 <= option_len(opt)) &&
417 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
418 break;
419 }
420 if (match)
421 break;
422 }
423 }
424 else
425 {
426 if (!(opt = option_find(mess, sz, o->opt, 1)))
427 continue;
428
429 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
430 }
431
432 if (match)
433 {
434 o->netid->next = netid;
435 netid = o->netid;
436 }
437 }
438
439 /* user-class options are, according to RFC3004, supposed to contain
440 a set of counted strings. Here we check that this is so (by seeing
441 if the counts are consistent with the overall option length) and if
442 so zero the counts so that we don't get spurious matches between
443 the vendor string and the counts. If the lengths don't add up, we
444 assume that the option is a single string and non RFC3004 compliant
445 and just do the substring match. dhclient provides these broken options.
446 The code, later, which sends user-class data to the lease-change script
447 relies on the transformation done here.
448 */
449
450 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
451 {
452 unsigned char *ucp = option_ptr(opt, 0);
453 int tmp, j;
454 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
455 if (j == option_len(opt))
456 for (j = 0; j < option_len(opt); j = tmp)
457 {
458 tmp = j + ucp[j] + 1;
459 ucp[j] = 0;
460 }
461 }
462
463 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
464 {
465 int mopt;
466
467 if (vendor->match_type == MATCH_VENDOR)
468 mopt = OPTION_VENDOR_ID;
469 else if (vendor->match_type == MATCH_USER)
470 mopt = OPTION_USER_CLASS;
471 else
472 continue;
473
474 if ((opt = option_find(mess, sz, mopt, 1)))
475 {
476 int i;
477 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
478 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
479 {
480 vendor->netid.next = netid;
481 netid = &vendor->netid;
482 break;
483 }
484 }
485 }
486
487 /* mark vendor-encapsulated options which match the client-supplied vendor class,
488 save client-supplied vendor class */
489 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
490 {
491 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
492 vendor_class_len = option_len(opt);
493 }
494 match_vendor_opts(opt, daemon->dhcp_opts);
495
496 if (option_bool(OPT_LOG_OPTS))
497 {
498 if (sanitise(opt, daemon->namebuff))
499 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
500 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
501 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
502 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100503
Simon Kelley3be34542004-09-11 19:12:13 +0100504 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100505
Simon Kelleycdeda282006-03-16 20:16:06 +0000506 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
507 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100508
509 /* set "known" tag for known hosts */
510 if (config)
511 {
512 known_id.net = "known";
513 known_id.next = netid;
514 netid = &known_id;
515 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100516 else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
517 mess->chaddr, mess->hlen, mess->htype, NULL))
518 {
519 known_id.net = "known-othernet";
520 known_id.next = netid;
521 netid = &known_id;
522 }
Simon Kelley26128d22004-11-14 16:43:54 +0000523
Simon Kelley316e2732010-01-22 20:16:09 +0000524 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100525 {
526 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000527 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000528 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100529
530 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100531 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100532 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000533
Simon Kelley26128d22004-11-14 16:43:54 +0000534 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000535 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000536
537 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
538
539 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000540 {
541 hostname = config->hostname;
542 domain = config->domain;
543 }
544
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100545 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000546 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100547 struct dhcp_netid_list *list;
548
549 for (list = config->netid; list; list = list->next)
550 {
551 list->list->next = netid;
552 netid = list->list;
553 }
Simon Kelley26128d22004-11-14 16:43:54 +0000554 }
555
556 /* Match incoming filename field as a netid. */
557 if (mess->file[0])
558 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000559 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
560 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
561 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000562 id.next = netid;
563 netid = &id;
564 }
Simon Kelley6b010842007-02-12 20:32:07 +0000565
566 /* Add "bootp" as a tag to allow different options, address ranges etc
567 for BOOTP clients */
568 bootp_id.net = "bootp";
569 bootp_id.next = netid;
570 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000571
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100572 tagif_netid = run_tag_if(netid);
573
Simon Kelley26128d22004-11-14 16:43:54 +0000574 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100575 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100576 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000577
Simon Kelley3d8df262005-08-29 12:19:27 +0100578 if (!message)
579 {
Simon Kelley9009d742008-11-14 20:04:27 +0000580 int nailed = 0;
581
Simon Kelley3d8df262005-08-29 12:19:27 +0100582 if (have_config(config, CONFIG_ADDR))
583 {
Simon Kelley9009d742008-11-14 20:04:27 +0000584 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100585 logaddr = &config->addr;
586 mess->yiaddr = config->addr;
587 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000588 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100589 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000590 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000591 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100592 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100593 else
594 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000595 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100596 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000597 {
598 if (lease)
599 {
600 /* lease exists, wrong network. */
601 lease_prune(lease, now);
602 lease = NULL;
603 }
Simon Kelleyc7be0162017-05-10 22:21:53 +0100604 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
Simon Kelleye17fb622006-01-14 20:33:46 +0000605 message = _("no address available");
606 }
607 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100608 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100609 }
610
Simon Kelley9009d742008-11-14 20:04:27 +0000611 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
612 message = _("wrong network");
613 else if (context->netid.net)
614 {
615 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100616 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100617 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100618
Simon Kelley4cb1b322012-02-06 14:30:41 +0000619 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100620
Simon Kelley9009d742008-11-14 20:04:27 +0000621 if (!message && !nailed)
622 {
623 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100624 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000625 break;
626 if (!id_list)
627 message = _("no address configured");
628 }
629
Simon Kelley7cebd202006-05-06 14:13:33 +0100630 if (!message &&
631 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000632 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000633 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000634
Simon Kelley3d8df262005-08-29 12:19:27 +0100635 if (!message)
636 {
637 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100638
Simon Kelleya9ab7322012-04-28 11:29:37 +0100639 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100640 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000641 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100642 /* infinite lease unless nailed in dhcp-host line. */
643 lease_set_expires(lease,
644 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
645 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000646 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100647
Simon Kelleya9df0e32017-04-28 22:43:00 +0100648 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000649 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +0100650 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100651 }
652 }
653
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100654 daemon->metrics[METRIC_BOOTP]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100655 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000656
Simon Kelley7de060b2011-08-26 17:24:52 +0100657 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100658 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000659
Roy Marples3f3adae2013-07-25 16:22:46 +0100660 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100661 {
662 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
663 int len = option_len(opt);
664 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100665 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100666
Simon Kelley9fed0f72012-08-30 11:43:35 +0100667 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100668 len -= 3;
669 op += 3;
670 pp = op;
671
Simon Kelley9fed0f72012-08-30 11:43:35 +0100672 /* NB, the following always sets at least one bit */
673 if (option_bool(OPT_FQDN_UPDATE))
674 {
675 if (fqdn_flags & 0x01)
676 {
677 fqdn_flags |= 0x02; /* set O */
678 fqdn_flags &= ~0x01; /* clear S */
679 }
680 fqdn_flags |= 0x08; /* set N */
681 }
682 else
683 {
684 if (!(fqdn_flags & 0x01))
685 fqdn_flags |= 0x03; /* set S and O */
686 fqdn_flags &= ~0x08; /* clear N */
687 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100688
689 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100690 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100691 {
692 memcpy(pq, op+1, *op);
693 pq += *op;
694 op += (*op)+1;
695 *(pq++) = '.';
696 }
697 else
698 {
699 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000700 if (len > 0 && op[len-1] == 0)
701 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100702 pq += len + 1;
703 }
704
705 if (pq != daemon->dhcp_buff)
706 pq--;
707
708 *pq = 0;
709
Simon Kelley1f15b812009-10-13 17:49:32 +0100710 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100711 offer_hostname = client_hostname = daemon->dhcp_buff;
712 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000713 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000714 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000715 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100716 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000717 /* Microsoft clients are broken, and need zero-terminated strings
718 in options. We detect this state here, and do the same in
719 any options we send */
720 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
721 borken_opt = 1;
722 else
723 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100724 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100725 client_hostname = daemon->dhcp_buff;
726 }
727
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100728 if (client_hostname && option_bool(OPT_LOG_OPTS))
729 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
Simon Kelleyc8226202018-08-08 23:46:03 +0100730
Simon Kelley7622fc02009-06-04 20:32:05 +0100731
Simon Kelley3d8df262005-08-29 12:19:27 +0100732 if (have_config(config, CONFIG_NAME))
733 {
734 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000735 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000736 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000737 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100738 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000739 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100740 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100741 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100742 {
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100743 struct dhcp_match_name *m;
744 size_t nl;
745
Simon Kelley9009d742008-11-14 20:04:27 +0000746 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100747
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100748 if ((nl = strlen(client_hostname)) != 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100749 {
750 hostname = client_hostname;
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100751
Simon Kelley5aabfc72007-08-29 11:24:47 +0100752 if (!config)
753 {
754 /* Search again now we have a hostname.
755 Only accept configs without CLID and HWADDR here, (they won't match)
756 to avoid impersonation by name. */
757 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
758 mess->chaddr, mess->hlen,
759 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000760 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000761 {
762 config = new;
763 /* set "known" tag for known hosts */
764 known_id.net = "known";
765 known_id.next = netid;
766 netid = &known_id;
767 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100768 }
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100769
770 for (m = daemon->dhcp_name_match; m; m = m->next)
771 {
772 size_t ml = strlen(m->name);
773 char save = 0;
774
775 if (nl < ml)
776 continue;
777 if (nl > ml)
778 {
779 save = client_hostname[ml];
780 client_hostname[ml] = 0;
781 }
782
783 if (hostname_isequal(client_hostname, m->name) &&
784 (save == 0 || m->wildcard))
785 {
786 m->netid->next = netid;
787 netid = m->netid;
788 }
789
790 if (save != 0)
791 client_hostname[ml] = save;
792 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100793 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000794 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100795
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100796 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100797 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100798 struct dhcp_netid_list *list;
799
800 for (list = config->netid; list; list = list->next)
801 {
802 list->list->next = netid;
803 netid = list->list;
804 }
Simon Kelleya2226412004-05-13 20:27:08 +0100805 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100806
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100807 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100808
Simon Kelley26128d22004-11-14 16:43:54 +0000809 /* if all the netids in the ignore list are present, ignore this client */
810 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100811 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000812 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100813
814 /* If configured, we can override the server-id to be the address of the relay,
815 so that all traffic goes via the relay and can pick up agent-id info. This can be
816 configured for all relays, or by address. */
817 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
818 {
819 if (!daemon->override_relays)
820 override = mess->giaddr;
821 else
822 {
823 struct addr_list *l;
824 for (l = daemon->override_relays; l; l = l->next)
825 if (l->addr.s_addr == mess->giaddr.s_addr)
826 break;
827 if (l)
828 override = mess->giaddr;
829 }
830 }
831
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100832 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
833 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000834 clid = NULL;
835
Simon Kelley7622fc02009-06-04 20:32:05 +0100836 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100837 if (daemon->enable_pxe &&
838 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100839 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
840 {
841 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
842 {
843 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
844 uuid = pxe_uuid;
845 }
846
847 /* Check if this is really a PXE bootserver request, and handle specially if so. */
848 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
849 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
850 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
851 {
852 struct pxe_service *service;
853 int type = option_uint(opt, 0, 2);
854 int layer = option_uint(opt, 2, 2);
855 unsigned char save71[4];
856 struct dhcp_opt opt71;
857
Simon Kelley1f15b812009-10-13 17:49:32 +0100858 if (ignore)
859 return 0;
860
Simon Kelley7622fc02009-06-04 20:32:05 +0100861 if (layer & 0x8000)
862 {
863 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
864 return 0;
865 }
866
867 memcpy(save71, option_ptr(opt, 0), 4);
868
869 for (service = daemon->pxe_services; service; service = service->next)
870 if (service->type == type)
871 break;
872
Simon Kelley549b1a42015-05-20 20:20:24 +0100873 for (; context; context = context->current)
874 if (match_netid(context->filter, tagif_netid, 1) &&
875 is_same_net(mess->ciaddr, context->start, context->netmask))
876 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100877
Simon Kelley549b1a42015-05-20 20:20:24 +0100878 if (!service || !service->basename || !context)
879 return 0;
880
Simon Kelleya9df0e32017-04-28 22:43:00 +0100881 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100882
883 mess->yiaddr = mess->ciaddr;
884 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000885 if (service->sname)
886 mess->siaddr = a_record_from_hosts(service->sname, now);
887 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100888 mess->siaddr = service->server;
889 else
890 mess->siaddr = context->local;
891
Matthias Andreef77700a2017-05-21 22:36:09 +0100892 if (strchr(service->basename, '.'))
893 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100894 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100895 else
896 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100897 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100898
Simon Kelley7622fc02009-06-04 20:32:05 +0100899 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
900 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
901 pxe_misc(mess, end, uuid);
902
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100903 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100904 opt71.val = save71;
905 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
906 opt71.len = 4;
907 opt71.flags = DHOPT_VENDOR_MATCH;
908 opt71.netid = NULL;
909 opt71.next = daemon->dhcp_opts;
910 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
911
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100912 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000913 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100914 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100915 }
916
917 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
918 {
919 pxearch = option_uint(opt, 0, 2);
920
Simon Kelley316e2732010-01-22 20:16:09 +0000921 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000922 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100923 {
Simon Kelley28866e92011-02-14 20:19:14 +0000924 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100925 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100926
Simon Kelley28866e92011-02-14 20:19:14 +0000927 for (tmp = context; tmp; tmp = tmp->current)
928 if ((tmp->flags & CONTEXT_PROXY) &&
929 match_netid(tmp->filter, tagif_netid, 1))
930 break;
931
932 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100933 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100934 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100935 int redirect4011 = 0;
936
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100937 if (tmp->netid.net)
938 {
939 tmp->netid.next = netid;
940 tagif_netid = run_tag_if(&tmp->netid);
941 }
942
943 boot = find_boot(tagif_netid);
944
Simon Kelley28866e92011-02-14 20:19:14 +0000945 mess->yiaddr.s_addr = 0;
946 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
947 {
948 mess->ciaddr.s_addr = 0;
949 mess->flags |= htons(0x8000); /* broadcast */
950 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100951
Simon Kelleya9df0e32017-04-28 22:43:00 +0100952 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000953
Simon Kelley0a4a0492016-05-15 20:13:45 +0100954 /* Redirect EFI clients to port 4011 */
955 if (pxearch >= 6)
956 {
957 redirect4011 = 1;
958 mess->siaddr = tmp->local;
959 }
960
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100961 /* Returns true if only one matching service is available. On port 4011,
962 it also inserts the boot file and server name. */
963 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100964
965 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000966 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000967 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100968 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100969 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000970 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100971 else if (boot->tftp_sname)
972 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000973
974 if (boot->file)
Petr Menšík47b45b22018-08-15 18:17:00 +0200975 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley28866e92011-02-14 20:19:14 +0000976 }
977
978 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
979 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100980 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Simon Kelley28866e92011-02-14 20:19:14 +0000981 pxe_misc(mess, end, uuid);
982 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +0100983 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +0100984 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
985
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100986 daemon->metrics[METRIC_PXE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100987 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000988 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +0100989 if (!ignore)
990 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100991 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100992 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100993 }
994 }
995 }
996
997 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000998 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100999 return 0;
1000
Simon Kelleybb01cb92004-12-13 20:56:23 +00001001 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001002 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001003 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001004 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +00001005 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001006 }
1007
Simon Kelley3be34542004-09-11 19:12:13 +01001008 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001009 {
Simon Kelley44a2a312004-03-10 20:04:35 +00001010 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +00001011 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001012 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001013 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001014
Simon Kelley44a2a312004-03-10 20:04:35 +00001015 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001016 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +00001017
Simon Kelleybb01cb92004-12-13 20:56:23 +00001018 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +00001019 return 0;
1020
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001021 daemon->metrics[METRIC_DHCPDECLINE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001022 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 +00001023
1024 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
1025 lease_prune(lease, now);
1026
Simon Kelley33820b72004-04-03 21:10:00 +01001027 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +00001028 config->addr.s_addr == option_addr(opt).s_addr)
1029 {
Simon Kelley849a8352006-06-09 21:02:31 +01001030 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +01001031 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001032 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +01001033 config->flags |= CONFIG_DECLINED;
1034 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +00001035 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001036 else
1037 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +01001038 for (; context; context = context->current)
1039 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +00001040
1041 return 0;
1042
1043 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001044 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +01001045 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001046 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001047 return 0;
1048
Simon Kelley44a2a312004-03-10 20:04:35 +00001049 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
1050 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001051 else
Simon Kelleyb8187c82005-11-26 21:46:27 +00001052 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001053
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001054 daemon->metrics[METRIC_DHCPRELEASE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001055 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001056
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001057 return 0;
1058
1059 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001060 if (ignore || have_config(config, CONFIG_DISABLE))
1061 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001062 if (option_bool(OPT_QUIET_DHCP))
1063 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001064 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001065 opt = NULL;
1066 }
1067 else
1068 {
1069 struct in_addr addr, conf;
1070
Simon Kelley1a6bca82008-07-11 11:11:42 +01001071 addr.s_addr = conf.s_addr = 0;
1072
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001073 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1074 addr = option_addr(opt);
1075
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001076 if (have_config(config, CONFIG_ADDR))
1077 {
Simon Kelley849a8352006-06-09 21:02:31 +01001078 char *addrs = inet_ntoa(config->addr);
1079
Simon Kelley9009d742008-11-14 20:04:27 +00001080 if ((ltmp = lease_find_by_addr(config->addr)) &&
1081 ltmp != lease &&
1082 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001083 {
1084 int len;
1085 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1086 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001087 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +01001088 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001089 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001090 else
1091 {
1092 struct dhcp_context *tmp;
1093 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001094 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001095 break;
1096 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001097 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 +01001098 else if (have_config(config, CONFIG_DECLINED) &&
1099 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001100 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001101 else
1102 conf = config->addr;
1103 }
1104 }
1105
1106 if (conf.s_addr)
1107 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001108 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001109 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001110 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001111 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001112 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001113 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001114 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001115 else if (emac_len == 0)
1116 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001117 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001118 message = _("no address available");
1119 }
1120
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001121 daemon->metrics[METRIC_DHCPDISCOVER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001122 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 +01001123
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001124 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001125 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001126
Simon Kelleycdeda282006-03-16 20:16:06 +00001127 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001128 {
1129 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001130 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001131 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001132
Simon Kelley4cb1b322012-02-06 14:30:41 +00001133 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001134 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001135
1136 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1137 {
1138 rapid_commit = 1;
1139 goto rapid_commit;
1140 }
1141
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001142 daemon->metrics[METRIC_DHCPOFFER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001143 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001144
Simon Kelley824af852008-02-12 20:43:05 +00001145 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001146 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001147 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001148 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001149 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001150 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001151 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001152 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001153
Simon Kelley7de060b2011-08-26 17:24:52 +01001154 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001155
1156
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001157 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001158 if (ignore || have_config(config, CONFIG_DISABLE))
1159 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001160 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001161 {
1162 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001163 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001164
Simon Kelley4011c4e2006-10-28 16:26:19 +01001165 /* send vendor and user class info for new or recreated lease */
1166 do_classes = 1;
1167
Simon Kelleybb01cb92004-12-13 20:56:23 +00001168 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001169 {
Simon Kelley3be34542004-09-11 19:12:13 +01001170 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001171 selecting = 1;
1172
Simon Kelley1a6bca82008-07-11 11:11:42 +01001173 if (override.s_addr != 0)
1174 {
1175 if (option_addr(opt).s_addr != override.s_addr)
1176 return 0;
1177 }
Simon Kelley9009d742008-11-14 20:04:27 +00001178 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001179 {
1180 for (; context; context = context->current)
1181 if (context->local.s_addr == option_addr(opt).s_addr)
1182 break;
1183
1184 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001185 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001186 /* Handle very strange configs where clients have more than one route to the server.
1187 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1188 Have to set override to make sure we echo back the correct server-id */
1189 struct irec *intr;
1190
Simon Kelley115ac3e2013-05-20 11:28:32 +01001191 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001192
1193 for (intr = daemon->interfaces; intr; intr = intr->next)
1194 if (intr->addr.sa.sa_family == AF_INET &&
1195 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1196 intr->tftp_ok)
1197 break;
1198
1199 if (intr)
1200 override = intr->addr.in.sin_addr;
1201 else
1202 {
1203 /* In auth mode, a REQUEST sent to the wrong server
1204 should be faulted, so that the client establishes
1205 communication with us, otherwise, silently ignore. */
1206 if (!option_bool(OPT_AUTHORITATIVE))
1207 return 0;
1208 message = _("wrong server-ID");
1209 }
Simon Kelley9009d742008-11-14 20:04:27 +00001210 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001211 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001212
Simon Kelley3be34542004-09-11 19:12:13 +01001213 /* If a lease exists for this host and another address, squash it. */
1214 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1215 {
1216 lease_prune(lease, now);
1217 lease = NULL;
1218 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001219 }
Simon Kelley3be34542004-09-11 19:12:13 +01001220 else
1221 {
1222 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001223 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001224 return 0;
1225
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001226 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001227 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001228 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001229 }
1230 else
1231 {
1232 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001233 /* Check existing lease for this address.
1234 We allow it to be missing if dhcp-authoritative mode
1235 as long as we can allocate the lease now - checked below.
1236 This makes for a smooth recovery from a lost lease DB */
1237 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001238 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001239 {
Simon Kelley28866e92011-02-14 20:19:14 +00001240 /* A client rebinding will broadcast the request, so we may see it even
1241 if the lease is held by another server. Just ignore it in that case.
1242 If the request is unicast to us, then somethings wrong, NAK */
1243 if (!unicast_dest)
1244 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001245 message = _("lease not found");
1246 /* ensure we broadcast NAK */
1247 unicast_dest = 0;
1248 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001249
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001250 /* desynchronise renewals */
1251 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001252 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001253 }
Simon Kelley734d5312018-03-23 23:09:53 +00001254
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001255 daemon->metrics[METRIC_DHCPREQUEST]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001256 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001257
1258 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001259 if (!message)
1260 {
1261 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001262 struct dhcp_context *tmp = NULL;
1263
1264 if (have_config(config, CONFIG_ADDR))
1265 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001266 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001267 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001268
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001269 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001270 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001271 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001272 message = _("wrong network");
1273 /* ensure we broadcast NAK */
1274 unicast_dest = 0;
1275 }
1276
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001277 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001278 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001279 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001280 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001281
Simon Kelleydfa666f2004-08-02 18:27:27 +01001282 /* Check if a new static address has been configured. Be very sure that
1283 when the client does DISCOVER, it will get the static address, otherwise
1284 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001285 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001286 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001287 (!have_config(config, CONFIG_DECLINED) ||
1288 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001289 config->addr.s_addr != mess->yiaddr.s_addr &&
1290 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001291 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001292
1293 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001294 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001295 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001296
Simon Kelley9009d742008-11-14 20:04:27 +00001297 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1298 {
1299 /* If a host is configured with more than one MAC address, it's OK to 'nix
1300 a lease from one of it's MACs to give the address to another. */
1301 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1302 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001303 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001304 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1305 inet_ntoa(ltmp->addr));
1306 lease = ltmp;
1307 }
Simon Kelley16972692006-10-16 20:04:18 +01001308 else
Simon Kelley9009d742008-11-14 20:04:27 +00001309 message = _("address in use");
1310 }
1311
1312 if (!message)
1313 {
1314 if (emac_len == 0)
1315 message = _("no unique-id");
1316
1317 else if (!lease)
1318 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001319 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001320 do_classes = 1;
1321 else
1322 message = _("no leases left");
1323 }
Simon Kelley16972692006-10-16 20:04:18 +01001324 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001325 }
Simon Kelley16972692006-10-16 20:04:18 +01001326
Simon Kelley44a2a312004-03-10 20:04:35 +00001327 if (message)
1328 {
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001329 daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
Simon Kelley734d5312018-03-23 23:09:53 +00001330 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1331
1332 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1333 if (rapid_commit)
1334 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001335
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001336 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001337 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001338 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001339 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001340 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001341 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1342 a distant subnet which unicast a REQ to us won't work. */
1343 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1344 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1345 {
1346 mess->flags |= htons(0x8000); /* broadcast */
1347 mess->ciaddr.s_addr = 0;
1348 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001349 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001350 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001351 {
Simon Kelley316e2732010-01-22 20:16:09 +00001352 if (context->netid.net)
1353 {
1354 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001355 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001356 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001357
Simon Kelley4cb1b322012-02-06 14:30:41 +00001358 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001359
Simon Kelleydcffad22012-04-24 15:25:18 +01001360 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001361 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001362 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001363 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001364
Simon Kelleydcffad22012-04-24 15:25:18 +01001365#ifdef HAVE_SCRIPT
1366 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001367 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001368 struct dhcp_netid *n;
1369
1370 if (mess->giaddr.s_addr)
1371 lease->giaddr = mess->giaddr;
1372
1373 free(lease->extradata);
1374 lease->extradata = NULL;
1375 lease->extradata_size = lease->extradata_len = 0;
1376
1377 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1378 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1379 add_extradata_opt(lease, oui);
1380 add_extradata_opt(lease, serial);
1381 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001382
1383 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1384 {
1385 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1386 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1387 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1388 }
1389 else
1390 {
1391 add_extradata_opt(lease, NULL);
1392 add_extradata_opt(lease, NULL);
1393 add_extradata_opt(lease, NULL);
1394 }
1395
ZHAO Yuf89cae32016-12-22 22:32:31 +00001396 /* DNSMASQ_REQUESTED_OPTIONS */
1397 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1398 {
1399 int len = option_len(opt);
1400 unsigned char *rop = option_ptr(opt, 0);
1401 char *q = daemon->namebuff;
1402 int i;
1403 for (i = 0; i < len; i++)
1404 {
1405 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1406 }
1407 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1408 }
1409 else
1410 {
1411 add_extradata_opt(lease, NULL);
1412 }
1413
Simon Kelleydcffad22012-04-24 15:25:18 +01001414 /* space-concat tag set */
1415 if (!tagif_netid)
1416 add_extradata_opt(lease, NULL);
1417 else
1418 for (n = tagif_netid; n; n = n->next)
1419 {
1420 struct dhcp_netid *n1;
1421 /* kill dupes */
1422 for (n1 = n->next; n1; n1 = n1->next)
1423 if (strcmp(n->net, n1->net) == 0)
1424 break;
1425 if (!n1)
1426 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1427 }
1428
1429 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1430 {
1431 int len = option_len(opt);
1432 unsigned char *ucp = option_ptr(opt, 0);
1433 /* If the user-class option started as counted strings, the first byte will be zero. */
1434 if (len != 0 && ucp[0] == 0)
1435 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001436 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001437 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001438 }
Simon Kelley316e2732010-01-22 20:16:09 +00001439#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001440 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001441
1442 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1443 {
1444 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001445 hostname = client_hostname;
1446 hostname_auth = 1;
1447 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001448
Simon Kelley824af852008-02-12 20:43:05 +00001449 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001450 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001451
Simon Kelley832af0b2007-01-21 20:01:28 +00001452 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1453 if (!hostname_auth)
1454 {
1455 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001456 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001457 break;
1458 if (id_list)
1459 hostname = NULL;
1460 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001461
1462 /* Last ditch, if configured, generate hostname from mac address */
1463 if (!hostname && emac_len != 0)
1464 {
1465 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1466 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1467 break;
1468 if (id_list)
1469 {
1470 int i;
1471
1472 hostname = daemon->dhcp_buff;
1473 /* buffer is 256 bytes, 3 bytes per octet */
1474 for (i = 0; (i < emac_len) && (i < 80); i++)
1475 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1476 hostname = daemon->dhcp_buff;
1477 }
1478 }
1479
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001480 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001481 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001482
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001483 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001484 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001485
1486 if (override.s_addr != 0)
1487 lease->override = override;
1488 else
1489 override = lease->override;
1490
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001491 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001492 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001493
Simon Kelleya9df0e32017-04-28 22:43:00 +01001494 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001495 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001496 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001497 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001498 if (rapid_commit)
1499 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1500 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001501 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001502 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001503
Simon Kelley7de060b2011-08-26 17:24:52 +01001504 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001505
1506 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001507 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001508 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001509
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001510 daemon->metrics[METRIC_DHCPINFORM]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001511 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001512
Simon Kelley73a08a22009-02-05 20:28:08 +00001513 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001514 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001515
1516 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001517 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001518
Simon Kelley5aabfc72007-08-29 11:24:47 +01001519 /* Find a least based on IP address if we didn't
1520 get one from MAC address/client-d */
1521 if (!lease &&
1522 (lease = lease_find_by_addr(mess->ciaddr)) &&
1523 lease->hostname)
1524 hostname = lease->hostname;
1525
Simon Kelley0f371f92013-07-27 15:15:38 +01001526 if (!hostname)
1527 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001528
Simon Kelley73a08a22009-02-05 20:28:08 +00001529 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001530 {
1531 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001532 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001533 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001534
Simon Kelley4cb1b322012-02-06 14:30:41 +00001535 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001536
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001537 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001538 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001539
Simon Kelley3927da42008-07-20 15:10:39 +01001540 if (lease)
1541 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001542 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001543 if (override.s_addr != 0)
1544 lease->override = override;
1545 else
1546 override = lease->override;
1547 }
1548
Simon Kelleya9df0e32017-04-28 22:43:00 +01001549 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001550 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001551 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001552
1553 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1554 we supply a utility which makes DHCPINFORM requests to get this information.
1555 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1556 which won't be true for ordinary clients, but will be true for the
1557 dhcp_lease_time utility. */
1558 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1559 {
1560 if (lease->expires == 0)
1561 time = 0xffffffff;
1562 else
1563 time = (unsigned int)difftime(lease->expires, now);
1564 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1565 }
1566
Simon Kelley9009d742008-11-14 20:04:27 +00001567 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001568 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001569
Simon Kelley5aabfc72007-08-29 11:24:47 +01001570 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001571 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001572 }
1573
1574 return 0;
1575}
1576
Simon Kelley6b010842007-02-12 20:32:07 +00001577/* find a good value to use as MAC address for logging and address-allocation hashing.
1578 This is normally just the chaddr field from the DHCP packet,
1579 but eg Firewire will have hlen == 0 and use the client-id instead.
1580 This could be anything, but will normally be EUI64 for Firewire.
1581 We assume that if the first byte of the client-id equals the htype byte
1582 then the client-id is using the usual encoding and use the rest of the
1583 client-id: if not we can use the whole client-id. This should give
1584 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001585unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001586 int clid_len, unsigned char *clid, int *len_out)
1587{
1588 if (hwlen == 0 && clid && clid_len > 3)
1589 {
1590 if (clid[0] == hwtype)
1591 {
1592 *len_out = clid_len - 1 ;
1593 return clid + 1;
1594 }
1595
1596#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1597 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1598 {
1599 *len_out = clid_len - 1 ;
1600 return clid + 1;
1601 }
1602#endif
1603
1604 *len_out = clid_len;
1605 return clid;
1606 }
1607
1608 *len_out = hwlen;
1609 return hwaddr;
1610}
1611
Simon Kelley824af852008-02-12 20:43:05 +00001612static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001613{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001614 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001615
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001616 if (opt)
1617 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001618 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001619 if (req_time < 120 )
1620 req_time = 120; /* sanity */
1621 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1622 time = req_time;
1623 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001624
1625 return time;
1626}
1627
Simon Kelley73a08a22009-02-05 20:28:08 +00001628static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001629{
Simon Kelley73a08a22009-02-05 20:28:08 +00001630 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001631 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001632 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001633 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001634 else
1635 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001636}
1637
Simon Kelleyf2621c72007-04-29 19:47:21 +01001638static int sanitise(unsigned char *opt, char *buf)
1639{
1640 char *p;
1641 int i;
1642
1643 *buf = 0;
1644
1645 if (!opt)
1646 return 0;
1647
Simon Kelley1a6bca82008-07-11 11:11:42 +01001648 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001649
1650 for (i = option_len(opt); i > 0; i--)
1651 {
1652 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001653 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001654 *buf++ = c;
1655 }
1656 *buf = 0; /* add terminator */
1657
1658 return 1;
1659}
1660
Simon Kelley316e2732010-01-22 20:16:09 +00001661#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001662static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1663{
1664 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001665 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001666 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001667 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001668}
1669#endif
1670
Simon Kelley5aabfc72007-08-29 11:24:47 +01001671static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001672 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001673{
Simon Kelley16972692006-10-16 20:04:18 +01001674 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001675
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001676 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001677 return;
1678
Simon Kelley16972692006-10-16 20:04:18 +01001679 /* addr may be misaligned */
1680 if (addr)
1681 memcpy(&a, addr, sizeof(a));
1682
Simon Kelley7622fc02009-06-04 20:32:05 +01001683 print_mac(daemon->namebuff, ext_mac, mac_len);
1684
Simon Kelley28866e92011-02-14 20:19:14 +00001685 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001686 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001687 ntohl(xid),
1688 type,
1689 interface,
1690 addr ? inet_ntoa(a) : "",
1691 addr ? " " : "",
1692 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001693 string ? string : "",
1694 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001695 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001696 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001697 type,
1698 interface,
1699 addr ? inet_ntoa(a) : "",
1700 addr ? " " : "",
1701 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001702 string ? string : "",
1703 err ? err : "");
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001704
1705#ifdef HAVE_UBUS
1706 if (!strcmp(type, "DHCPACK"))
1707 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1708 else if (!strcmp(type, "DHCPRELEASE"))
1709 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1710#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001711}
1712
Simon Kelley7622fc02009-06-04 20:32:05 +01001713static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001714{
1715 while (*start != OPTION_END)
1716 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001717 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 +01001718
Simon Kelley4cb1b322012-02-06 14:30:41 +00001719 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1720 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001721 start += start[1] + 2;
1722 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001723}
1724
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001725static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001726{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001727 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001728 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001729 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001730 return NULL;
1731 else if (*p == OPTION_END)
1732 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001733 else if (*p == OPTION_PAD)
1734 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001735 else
1736 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001737 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001738 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001739 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001740 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001741 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001742 return NULL; /* malformed packet */
1743 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001744 return p;
1745 p += opt_len + 2;
1746 }
1747 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001748}
1749
Simon Kelleycdeda282006-03-16 20:16:06 +00001750static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001751{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001752 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001753
Simon Kelley3be34542004-09-11 19:12:13 +01001754 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001755 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1756 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001757
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001758 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001759 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001760 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001761
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001762 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001763 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001764 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1765 return ret;
1766
1767 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001768 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001769 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1770 return ret;
1771
1772 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001773}
1774
Simon Kelley4cb1b322012-02-06 14:30:41 +00001775static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001776{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001777 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001778 /* struct in_addr is network byte order */
1779 struct in_addr ret;
1780
Simon Kelley4cb1b322012-02-06 14:30:41 +00001781 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001782
1783 return ret;
1784}
1785
Simon Kelley7622fc02009-06-04 20:32:05 +01001786static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001787{
1788 /* this worries about unaligned data and byte order */
1789 unsigned int ret = 0;
1790 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001791 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001792
1793 for (i = 0; i < size; i++)
1794 ret = (ret << 8) | *p++;
1795
1796 return ret;
1797}
1798
1799static unsigned char *dhcp_skip_opts(unsigned char *start)
1800{
1801 while (*start != 0)
1802 start += start[1] + 2;
1803 return start;
1804}
1805
1806/* only for use when building packet: doesn't check for bad data. */
1807static unsigned char *find_overload(struct dhcp_packet *mess)
1808{
1809 unsigned char *p = &mess->options[0] + sizeof(u32);
1810
1811 while (*p != 0)
1812 {
1813 if (*p == OPTION_OVERLOAD)
1814 return p;
1815 p += p[1] + 2;
1816 }
1817 return NULL;
1818}
1819
Simon Kelley7de060b2011-08-26 17:24:52 +01001820static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1821{
1822 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1823 unsigned char *overload;
1824 size_t ret;
1825
1826 /* move agent_id back down to the end of the packet */
1827 if (agent_id)
1828 {
1829 memmove(p, agent_id, real_end - agent_id);
1830 p += real_end - agent_id;
1831 memset(p, 0, real_end - p); /* in case of overlap */
1832 }
1833
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001834 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001835 overload = find_overload(mess);
1836
1837 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001838 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001839 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001840 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001841 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001842 }
Simon Kelley28866e92011-02-14 20:19:14 +00001843 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001844 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1845
1846 if (overload && (option_uint(overload, 0, 1) & 2))
1847 {
1848 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001849 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001850 log_options(mess->sname, mess->xid);
1851 }
Simon Kelley28866e92011-02-14 20:19:14 +00001852 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001853 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1854
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001855
1856 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001857
Simon Kelley28866e92011-02-14 20:19:14 +00001858 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001859 {
1860 if (mess->siaddr.s_addr != 0)
1861 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1862
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001863 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1864 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1865
Simon Kelley7622fc02009-06-04 20:32:05 +01001866 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1867 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001868
1869 ret = (size_t)(p - (unsigned char *)mess);
1870
1871 if (ret < MIN_PACKETSZ)
1872 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001873
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001874 return ret;
1875}
1876
1877static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1878{
1879 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1880
1881 if (p + len + 3 >= end)
1882 /* not enough space in options area, try and use overload, if poss */
1883 {
1884 unsigned char *overload;
1885
1886 if (!(overload = find_overload(mess)) &&
1887 (mess->file[0] == 0 || mess->sname[0] == 0))
1888 {
1889 /* attempt to overload fname and sname areas, we've reserved space for the
1890 overflow option previuously. */
1891 overload = p;
1892 *(p++) = OPTION_OVERLOAD;
1893 *(p++) = 1;
1894 }
1895
1896 p = NULL;
1897
1898 /* using filename field ? */
1899 if (overload)
1900 {
1901 if (mess->file[0] == 0)
1902 overload[2] |= 1;
1903
1904 if (overload[2] & 1)
1905 {
1906 p = dhcp_skip_opts(mess->file);
1907 if (p + len + 3 >= mess->file + sizeof(mess->file))
1908 p = NULL;
1909 }
1910
1911 if (!p)
1912 {
1913 /* try to bring sname into play (it may be already) */
1914 if (mess->sname[0] == 0)
1915 overload[2] |= 2;
1916
1917 if (overload[2] & 2)
1918 {
1919 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001920 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001921 p = NULL;
1922 }
1923 }
1924 }
1925
1926 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001927 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 +00001928 }
1929
1930 if (p)
1931 {
1932 *(p++) = opt;
1933 *(p++) = len;
1934 }
1935
1936 return p;
1937}
1938
1939static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1940{
1941 int i;
1942 unsigned char *p = free_space(mess, end, opt, len);
1943
1944 if (p)
1945 for (i = 0; i < len; i++)
1946 *(p++) = val >> (8 * (len - (i + 1)));
1947}
1948
1949static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1950 char *string, int null_term)
1951{
1952 unsigned char *p;
1953 size_t len = strlen(string);
1954
1955 if (null_term && len != 255)
1956 len++;
1957
1958 if ((p = free_space(mess, end, opt, len)))
1959 memcpy(p, string, len);
1960}
1961
1962/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001963static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001964{
1965 int len = opt->len;
1966
1967 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1968 len++;
1969
1970 if (p && len != 0)
1971 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001972 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001973 {
1974 int j;
1975 struct in_addr *a = (struct in_addr *)opt->val;
1976 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1977 {
1978 /* zero means "self" (but not in vendorclass options.) */
1979 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001980 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001981 else
1982 memcpy(p, a, INADDRSZ);
1983 p += INADDRSZ;
1984 }
1985 }
1986 else
Simon Kelley625ac282013-07-02 21:19:32 +01001987 /* empty string may be extended to "\0" by null_term */
1988 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001989 }
1990 return len;
1991}
Simon Kelley7622fc02009-06-04 20:32:05 +01001992
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001993static int in_list(unsigned char *list, int opt)
1994{
1995 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001996
1997 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001998 if (!list)
1999 return 1;
2000
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002001 for (i = 0; list[i] != OPTION_END; i++)
2002 if (opt == list[i])
2003 return 1;
2004
2005 return 0;
2006}
2007
Simon Kelley7de060b2011-08-26 17:24:52 +01002008static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002009{
Simon Kelley7de060b2011-08-26 17:24:52 +01002010 struct dhcp_opt *opts;
2011
2012 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
2013 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
2014 return opts;
2015
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002016 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002017}
2018
Simon Kelley6b010842007-02-12 20:32:07 +00002019/* mark vendor-encapsulated options which match the client-supplied or
2020 config-supplied vendor class */
2021static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
2022{
2023 for (; dopt; dopt = dopt->next)
2024 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002025 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00002026 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00002027 {
2028 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00002029 if (dopt->u.vendor_class)
2030 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00002031 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00002032 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00002033 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002034 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00002035 break;
2036 }
2037 }
2038 }
2039}
2040
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002041static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
2042 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00002043{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002044 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01002045 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00002046 unsigned char *p;
2047
2048 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01002049 for (enc_len = 0, start = opt; opt; opt = opt->next)
2050 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002051 {
2052 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002053 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00002054 if (enc_len + new <= 255)
2055 enc_len += new;
2056 else
2057 {
2058 p = free_space(mess, end, encap, enc_len);
2059 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002060 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00002061 {
2062 len = do_opt(start, p + 2, NULL, null_term);
2063 *(p++) = start->opt;
2064 *(p++) = len;
2065 p += len;
2066 }
2067 enc_len = new;
2068 start = opt;
2069 }
2070 }
2071
2072 if (enc_len != 0 &&
2073 (p = free_space(mess, end, encap, enc_len + 1)))
2074 {
2075 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002076 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002077 {
2078 len = do_opt(start, p + 2, NULL, null_term);
2079 *(p++) = start->opt;
2080 *(p++) = len;
2081 p += len;
2082 }
2083 *p = OPTION_END;
2084 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002085
2086 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002087}
2088
Simon Kelley7622fc02009-06-04 20:32:05 +01002089static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002090{
Simon Kelley7622fc02009-06-04 20:32:05 +01002091 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002092
Simon Kelley7622fc02009-06-04 20:32:05 +01002093 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2094 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2095 memcpy(p, uuid, 17);
2096}
2097
2098static int prune_vendor_opts(struct dhcp_netid *netid)
2099{
2100 int force = 0;
2101 struct dhcp_opt *opt;
2102
2103 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2104 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2105 if (opt->flags & DHOPT_VENDOR_MATCH)
2106 {
2107 if (!match_netid(opt->netid, netid, 1))
2108 opt->flags &= ~DHOPT_VENDOR_MATCH;
2109 else if (opt->flags & DHOPT_FORCE)
2110 force = 1;
2111 }
2112 return force;
2113}
2114
Simon Kelley8628cd62016-05-10 17:31:48 +01002115
2116/* Many UEFI PXE implementations have badly broken menu code.
2117 If there's exactly one relevant menu item, we abandon the menu system,
2118 and jamb the data direct into the DHCP file, siaddr and sname fields.
2119 Note that in this case, we have to assume that layer zero would be requested
2120 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002121static 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 +01002122{
2123 struct pxe_service *service, *found;
2124
2125 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002126 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002127 return 0;
2128
2129 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002130 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002131 {
2132 if (found)
2133 return 0; /* More than one relevant menu item */
2134
2135 found = service;
2136 }
2137
2138 if (!found)
2139 return 0; /* No relevant menu items. */
2140
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002141 if (!pxe)
2142 return 1;
2143
Simon Kelley8628cd62016-05-10 17:31:48 +01002144 if (found->sname)
2145 {
2146 mess->siaddr = a_record_from_hosts(found->sname, now);
2147 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2148 }
2149 else
2150 {
2151 if (found->server.s_addr != 0)
2152 mess->siaddr = found->server;
2153 else
2154 mess->siaddr = local;
2155
2156 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2157 }
2158
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002159 snprintf((char *)mess->file, sizeof(mess->file),
2160 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2161
Simon Kelley8628cd62016-05-10 17:31:48 +01002162 return 1;
2163}
2164
Simon Kelley751d6f42012-02-10 15:24:51 +00002165static 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 +01002166{
2167#define NUM_OPTS 4
2168
2169 unsigned char *p, *q;
2170 struct pxe_service *service;
2171 static struct dhcp_opt *o, *ret;
2172 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002173 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002174
2175 /* We pass back references to these, hence they are declared static */
2176 static unsigned char discovery_control;
2177 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2178 static struct dhcp_opt *fake_opts = NULL;
2179
Simon Kelley316e2732010-01-22 20:16:09 +00002180 /* Disable multicast, since we don't support it, and broadcast
2181 unless we need it */
2182 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002183
2184 ret = daemon->dhcp_opts;
2185
2186 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2187 return ret;
2188
2189 for (i = 0; i < NUM_OPTS; i++)
2190 {
2191 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2192 fake_opts[i].netid = NULL;
2193 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2194 }
2195
2196 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2197 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002198 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002199
2200 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2201 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2202 {
2203 size_t len = strlen(service->menu);
2204 /* opt 43 max size is 255. encapsulated option has type and length
2205 bytes, so its max size is 253. */
2206 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2207 {
2208 *(p++) = service->type >> 8;
2209 *(p++) = service->type;
2210 *(p++) = len;
2211 memcpy(p, service->menu, len);
2212 p += len;
2213 i++;
2214 }
2215 else
2216 {
2217 toobig:
2218 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2219 return daemon->dhcp_opts;
2220 }
2221
Simon Kelley751d6f42012-02-10 15:24:51 +00002222 boot_server = service->basename ? local :
2223 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2224
Simon Kelley316e2732010-01-22 20:16:09 +00002225 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002226 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002227 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002228 goto toobig;
2229
2230 /* Boot service with known address - give it */
2231 *(q++) = service->type >> 8;
2232 *(q++) = service->type;
2233 *(q++) = 1;
2234 /* dest misaligned */
2235 memcpy(q, &boot_server.s_addr, INADDRSZ);
2236 q += INADDRSZ;
2237 }
2238 else if (service->type != 0)
2239 /* We don't know the server for a service type, so we'll
2240 allow the client to broadcast for it */
2241 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002242 }
2243
2244 /* if no prompt, wait forever if there's a choice */
2245 fake_prompt[0] = (i > 1) ? 255 : 0;
2246
2247 if (i == 0)
2248 discovery_control = 8; /* no menu - just use use mess->filename */
2249 else
2250 {
2251 ret = &fake_opts[j--];
2252 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2253 ret->val = (unsigned char *)daemon->dhcp_buff;
2254 ret->opt = SUBOPT_PXE_MENU;
2255
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002256 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002257 {
2258 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002259 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2260 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002261 ret->opt = SUBOPT_PXE_SERVERS;
2262 }
2263 }
2264
2265 for (o = daemon->dhcp_opts; o; o = o->next)
2266 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2267 break;
2268
2269 if (!o)
2270 {
2271 ret = &fake_opts[j--];
2272 ret->len = sizeof(fake_prompt);
2273 ret->val = fake_prompt;
2274 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2275 }
2276
Simon Kelley316e2732010-01-22 20:16:09 +00002277 ret = &fake_opts[j--];
2278 ret->len = 1;
2279 ret->opt = SUBOPT_PXE_DISCOVERY;
2280 ret->val= &discovery_control;
2281
Simon Kelley7622fc02009-06-04 20:32:05 +01002282 return ret;
2283}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002284
2285static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002286{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002287 memset(mess->sname, 0, sizeof(mess->sname));
2288 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002289 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002290 mess->siaddr.s_addr = 0;
2291}
Simon Kelleycdeda282006-03-16 20:16:06 +00002292
Simon Kelley7622fc02009-06-04 20:32:05 +01002293struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002294{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002295 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002296
2297 /* decide which dhcp-boot option we're using */
2298 for (boot = daemon->boot_config; boot; boot = boot->next)
2299 if (match_netid(boot->netid, netid, 0))
2300 break;
2301 if (!boot)
2302 /* No match, look for one without a netid */
2303 for (boot = daemon->boot_config; boot; boot = boot->next)
2304 if (match_netid(boot->netid, netid, 1))
2305 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002306
2307 return boot;
2308}
2309
2310static void do_options(struct dhcp_context *context,
2311 struct dhcp_packet *mess,
2312 unsigned char *end,
2313 unsigned char *req_options,
2314 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002315 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002316 struct dhcp_netid *netid,
2317 struct in_addr subnet_addr,
2318 unsigned char fqdn_flags,
2319 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002320 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002321 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002322 time_t now,
2323 unsigned int lease_time,
2324 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002325{
2326 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2327 struct dhcp_boot *boot;
2328 unsigned char *p;
2329 int i, len, force_encap = 0;
2330 unsigned char f0 = 0, s0 = 0;
2331 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002332 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002333 struct dhcp_netid *tagif;
2334 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002335
Simon Kelley4cb1b322012-02-06 14:30:41 +00002336 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002337 if (context)
2338 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002339 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002340
Simon Kelley7622fc02009-06-04 20:32:05 +01002341 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002342 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002343 {
2344 char *q = daemon->namebuff;
2345 for (i = 0; req_options[i] != OPTION_END; i++)
2346 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002347 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002348 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2349 "%d%s%s%s",
2350 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002351 strlen(s) != 0 ? ":" : "",
2352 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002353 req_options[i+1] == OPTION_END ? "" : ", ");
2354 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2355 {
2356 q = daemon->namebuff;
2357 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2358 }
2359 }
2360 }
2361
Simon Kelley7de060b2011-08-26 17:24:52 +01002362 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2363 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2364 break;
2365 if (id_list)
2366 mess->flags |= htons(0x8000); /* force broadcast */
2367
Simon Kelley73a08a22009-02-05 20:28:08 +00002368 if (context)
2369 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002370
2371 /* See if we can send the boot stuff as options.
2372 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002373 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002374 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002375 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002376 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002377 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002378 {
2379 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002380 {
Simon Kelley28866e92011-02-14 20:19:14 +00002381 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002382 req_options &&
2383 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002384 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2385 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002386 safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002387 }
2388
2389 if (boot->file)
2390 {
Simon Kelley28866e92011-02-14 20:19:14 +00002391 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002392 req_options &&
2393 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002394 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2395 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002396 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002397 }
2398
Simon Kelley7de060b2011-08-26 17:24:52 +01002399 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002400 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002401 else if (boot->tftp_sname)
2402 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002403 }
Simon Kelley824af852008-02-12 20:43:05 +00002404 else
2405 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002406 they're not explicitly asked for as options. OPTION_END is used
2407 as an internal way to specify siaddr without using dhcp-boot, for use in
2408 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002409 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002410 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002411 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002412 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002413 safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
Simon Kelley824af852008-02-12 20:43:05 +00002414 done_file = 1;
2415 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002416
Simon Kelley824af852008-02-12 20:43:05 +00002417 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002418 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002419 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002420 safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
Simon Kelley824af852008-02-12 20:43:05 +00002421 done_server = 1;
2422 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002423
Simon Kelley7de060b2011-08-26 17:24:52 +01002424 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002425 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002426 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002427
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002428 /* We don't want to do option-overload for BOOTP, so make the file and sname
2429 fields look like they are in use, even when they aren't. This gets restored
2430 at the end of this function. */
2431
Simon Kelley28866e92011-02-14 20:19:14 +00002432 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002433 {
2434 f0 = mess->file[0];
2435 mess->file[0] = 1;
2436 s0 = mess->sname[0];
2437 mess->sname[0] = 1;
2438 }
2439
2440 /* At this point, if mess->sname or mess->file are zeroed, they are available
2441 for option overload, reserve space for the overload option. */
2442 if (mess->file[0] == 0 || mess->sname[0] == 0)
2443 end -= 3;
2444
Simon Kelley3be34542004-09-11 19:12:13 +01002445 /* rfc3011 says this doesn't need to be in the requested options list. */
2446 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002447 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002448
2449 if (lease_time != 0xffffffff)
2450 {
2451 unsigned int t1val = lease_time/2;
2452 unsigned int t2val = (lease_time*7)/8;
2453 unsigned int hval;
2454
2455 /* If set by user, sanity check, so not longer than lease. */
2456 if ((opt = option_find2(OPTION_T1)))
2457 {
2458 hval = ntohl(*((unsigned int *)opt->val));
2459 if (hval < lease_time && hval > 2)
2460 t1val = hval;
2461 }
2462
2463 if ((opt = option_find2(OPTION_T2)))
2464 {
2465 hval = ntohl(*((unsigned int *)opt->val));
2466 if (hval < lease_time && hval > 2)
2467 t2val = hval;
2468 }
2469
Simon Kelley7c0f2542015-05-14 21:16:18 +01002470 /* ensure T1 is still < T2 */
2471 if (t2val <= t1val)
2472 t1val = t2val - 1;
2473
Simon Kelleyca85a282015-05-13 22:33:04 +01002474 while (fuzz > (t1val/8))
2475 fuzz = fuzz/2;
2476
2477 t1val -= fuzz;
2478 t2val -= fuzz;
2479
Simon Kelleyca85a282015-05-13 22:33:04 +01002480 option_put(mess, end, OPTION_T1, 4, t1val);
2481 option_put(mess, end, OPTION_T2, 4, t2val);
2482 }
2483
Simon Kelley73a08a22009-02-05 20:28:08 +00002484 /* replies to DHCPINFORM may not have a valid context */
2485 if (context)
2486 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002487 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002488 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2489
2490 /* May not have a "guessed" broadcast address if we got no packets via a relay
2491 from this net yet (ie just unicast renewals after a restart */
2492 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002493 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002494 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2495
2496 /* Same comments as broadcast apply, and also may not be able to get a sensible
2497 default when using subnet select. User must configure by steam in that case. */
2498 if (context->router.s_addr &&
2499 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002500 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002501 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2502
Simon Kelleya21e27b2013-02-17 16:41:35 +00002503 if (daemon->port == NAMESERVER_PORT &&
2504 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002505 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002506 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2507 }
Simon Kelley3be34542004-09-11 19:12:13 +01002508
Simon Kelley9009d742008-11-14 20:04:27 +00002509 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002510 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002511 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002512
Simon Kelley824af852008-02-12 20:43:05 +00002513 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002514 if (hostname)
2515 {
Simon Kelley824af852008-02-12 20:43:05 +00002516 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002517 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002518 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002519
2520 if (fqdn_flags != 0)
2521 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002522 len = strlen(hostname) + 3;
2523
Simon Kelley3d8df262005-08-29 12:19:27 +01002524 if (fqdn_flags & 0x04)
2525 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002526 else if (null_term)
2527 len++;
2528
Simon Kelley9009d742008-11-14 20:04:27 +00002529 if (domain)
2530 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002531 else if (fqdn_flags & 0x04)
2532 len--;
2533
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002534 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002535 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002536 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002537 *(p++) = 255;
2538 *(p++) = 255;
2539
2540 if (fqdn_flags & 0x04)
2541 {
Simon Kelley0549c732017-09-25 18:17:11 +01002542 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002543 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002544 {
Simon Kelley0549c732017-09-25 18:17:11 +01002545 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002546 *p++ = 0;
2547 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002548 }
2549 else
2550 {
2551 memcpy(p, hostname, strlen(hostname));
2552 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002553 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002554 {
2555 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002556 memcpy(p, domain, strlen(domain));
2557 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002558 }
2559 if (null_term)
2560 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002561 }
2562 }
2563 }
2564 }
2565
Simon Kelley6b010842007-02-12 20:32:07 +00002566 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002567 {
Simon Kelley824af852008-02-12 20:43:05 +00002568 int optno = opt->opt;
2569
Simon Kelley7de060b2011-08-26 17:24:52 +01002570 /* netids match and not encapsulated? */
2571 if (!(opt->flags & DHOPT_TAGOK))
2572 continue;
2573
Simon Kelley6b010842007-02-12 20:32:07 +00002574 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002575 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002576 continue;
2577
Simon Kelleyca85a282015-05-13 22:33:04 +01002578 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002579 if (optno == OPTION_CLIENT_FQDN ||
2580 optno == OPTION_MAXMESSAGE ||
2581 optno == OPTION_OVERLOAD ||
2582 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002583 optno == OPTION_END ||
2584 optno == OPTION_T1 ||
2585 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002586 continue;
2587
2588 if (optno == OPTION_SNAME && done_server)
2589 continue;
2590
2591 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002592 continue;
2593
Simon Kelley33820b72004-04-03 21:10:00 +01002594 /* For the options we have default values on
2595 dhc-option=<optionno> means "don't include this option"
2596 not "include a zero-length option" */
2597 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002598 (optno == OPTION_NETMASK ||
2599 optno == OPTION_BROADCAST ||
2600 optno == OPTION_ROUTER ||
2601 optno == OPTION_DNSSERVER ||
2602 optno == OPTION_DOMAINNAME ||
2603 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002604 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002605
2606 /* vendor-class comes from elsewhere for PXE */
2607 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2608 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002609
Simon Kelley7622fc02009-06-04 20:32:05 +01002610 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002611 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002612 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002613
Simon Kelley824af852008-02-12 20:43:05 +00002614 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002615 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002616 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002617 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2618
Simon Kelley6b010842007-02-12 20:32:07 +00002619 /* If we send a vendor-id, revisit which vendor-ops we consider
2620 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002621 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002622 {
2623 match_vendor_opts(p - 2, config_opts);
2624 done_vendor_class = 1;
2625 }
Simon Kelley6b010842007-02-12 20:32:07 +00002626 }
2627 }
2628
Simon Kelley73a08a22009-02-05 20:28:08 +00002629 /* Now send options to be encapsulated in arbitrary options,
2630 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002631 Also handle vendor-identifying vendor-encapsulated options,
2632 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002633 The may be more that one "outer" to do, so group
2634 all the options which match each outer in turn. */
2635 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002636 opt->flags &= ~DHOPT_ENCAP_DONE;
2637
2638 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002639 {
2640 int flags;
2641
2642 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2643 {
2644 int found = 0;
2645 struct dhcp_opt *o;
2646
2647 if (opt->flags & DHOPT_ENCAP_DONE)
2648 continue;
2649
2650 for (len = 0, o = config_opts; o; o = o->next)
2651 {
2652 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2653
2654 o->flags &= ~DHOPT_ENCAP_MATCH;
2655
2656 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2657 continue;
2658
2659 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002660 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002661 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2662 {
2663 o->flags |= DHOPT_ENCAP_MATCH;
2664 found = 1;
2665 len += do_opt(o, NULL, NULL, 0) + 2;
2666 }
2667 }
2668
2669 if (found)
2670 {
2671 if (flags & DHOPT_ENCAPSULATE)
2672 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2673 else if (len > 250)
2674 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2675 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2676 {
2677 int swap_ent = htonl(opt->u.encap);
2678 memcpy(p, &swap_ent, 4);
2679 p += 4;
2680 *(p++) = len;
2681 for (o = config_opts; o; o = o->next)
2682 if (o->flags & DHOPT_ENCAP_MATCH)
2683 {
2684 len = do_opt(o, p + 2, NULL, 0);
2685 *(p++) = o->opt;
2686 *(p++) = len;
2687 p += len;
2688 }
2689 }
2690 }
2691 }
2692 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002693
Simon Kelley7de060b2011-08-26 17:24:52 +01002694 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002695
Simon Kelley316e2732010-01-22 20:16:09 +00002696 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002697 {
2698 pxe_misc(mess, end, uuid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002699 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002700 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002701 }
2702
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002703 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2704 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2705 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2706 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2707 /* If we send vendor encapsulated options, and haven't already sent option 60,
2708 echo back the value we got from the client. */
2709 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2710
Simon Kelley7622fc02009-06-04 20:32:05 +01002711 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002712 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002713 {
2714 mess->file[0] = f0;
2715 mess->sname[0] = s0;
2716 }
2717}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002718
Floris Bos503c6092017-04-09 23:07:13 +01002719static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2720{
2721 struct delay_config *delay_conf;
2722
2723 /* Decide which delay_config option we're using */
2724 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2725 if (match_netid(delay_conf->netid, netid, 0))
2726 break;
2727
2728 if (!delay_conf)
2729 /* No match, look for one without a netid */
2730 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2731 if (match_netid(delay_conf->netid, netid, 1))
2732 break;
2733
2734 if (delay_conf)
2735 {
2736 if (!option_bool(OPT_QUIET_DHCP))
2737 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2738 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2739 }
2740}
2741
Simon Kelley7622fc02009-06-04 20:32:05 +01002742#endif
2743
2744
2745
2746
2747
2748
2749