blob: 5416f3e1536fda4e6936f4b7f18109289f5019a8 [file] [log] [blame]
Simon Kelleyd1ced3a2018-01-01 22:18:03 +00001/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010022#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000023
Simon Kelley316e2732010-01-22 20:16:09 +000024#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +000025static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
26#endif
Simon Kelley572b41e2011-02-18 18:11:18 +000027
Simon Kelleyf2621c72007-04-29 19:47:21 +010028static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000029static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000030static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000031static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
32static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
33 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034static struct in_addr option_addr(unsigned char *opt);
Rosen Penev50a28412017-06-27 22:27:02 +010035static unsigned int option_uint(unsigned char *opt, int offset, int size);
Simon Kelley7622fc02009-06-04 20:32:05 +010036static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +010037 int mac_len, char *interface, char *string, char *err, u32 xid);
Simon Kelleycdeda282006-03-16 20:16:06 +000038static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010039static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley7de060b2011-08-26 17:24:52 +010040static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
Simon Kelleya9df0e32017-04-28 22:43:00 +010041static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
Simon Kelleyaa63a212013-04-22 15:01:52 +010042static int in_list(unsigned char *list, int opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000043static void do_options(struct dhcp_context *context,
44 struct dhcp_packet *mess,
Rosen Penev50a28412017-06-27 22:27:02 +010045 unsigned char *end,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000046 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000047 char *hostname,
Rosen Penev50a28412017-06-27 22:27:02 +010048 char *domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000049 struct dhcp_netid *netid,
Simon Kelley7de060b2011-08-26 17:24:52 +010050 struct in_addr subnet_addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000051 unsigned char fqdn_flags,
Rosen Penev50a28412017-06-27 22:27:02 +010052 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +010053 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +010054 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +010055 time_t now,
56 unsigned int lease_time,
57 unsigned short fuzz);
Simon Kelley7622fc02009-06-04 20:32:05 +010058
Simon Kelley9009d742008-11-14 20:04:27 +000059
Simon Kelley6b010842007-02-12 20:32:07 +000060static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Rosen Penev50a28412017-06-27 22:27:02 +010061static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
Simon Kelley7622fc02009-06-04 20:32:05 +010062static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
63static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000064static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010065struct dhcp_boot *find_boot(struct dhcp_netid *netid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +010066static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
Floris Bos503c6092017-04-09 23:07:13 +010067static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
68
Simon Kelley824af852008-02-12 20:43:05 +000069size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelleyc7be0162017-05-10 22:21:53 +010070 size_t sz, time_t now, int unicast_dest, int loopback,
71 int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
Simon Kelley33820b72004-04-03 21:10:00 +010072{
Simon Kelley26128d22004-11-14 16:43:54 +000073 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000074 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010075 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000076 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000077 struct dhcp_netid_list *id_list;
Simon Kelley734d5312018-03-23 23:09:53 +000078 int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000079 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000080 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010081 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000082 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000083 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010084 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000085 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000086 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000087 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010088 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010089 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010090 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010091 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010092 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010093 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000094 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010095 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000096 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000097 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010098 unsigned char pxe_uuid[17];
Vladislav Grishenko99e88912013-11-26 11:02:29 +000099 unsigned char *oui = NULL, *serial = NULL;
100#ifdef HAVE_SCRIPT
101 unsigned char *class = NULL;
102#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100103
Simon Kelley1a6bca82008-07-11 11:11:42 +0100104 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000105
106 /* set tag with name == interface */
107 iface_id.net = iface_name;
108 iface_id.next = NULL;
109 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100110
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100112 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000113
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100114 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000115 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100116
Simon Kelley3be34542004-09-11 19:12:13 +0100117 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000118 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000119 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100120 u32 cookie = htonl(DHCP_COOKIE);
121
Simon Kelley3be34542004-09-11 19:12:13 +0100122 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100123 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100124 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100125
126 mess_type = option_uint(opt, 0, 1);
127
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100128 /* two things to note here: expand_buf may move the packet,
129 so reassign mess from daemon->packet. Also, the size
130 sent includes the IP and UDP headers, hence the magic "-28" */
131 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
132 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100133 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100134
135 if (size > DHCP_PACKET_MAX)
136 size = DHCP_PACKET_MAX;
137 else if (size < sizeof(struct dhcp_packet))
138 size = sizeof(struct dhcp_packet);
139
140 if (expand_buf(&daemon->dhcp_packet, size))
141 {
Simon Kelley824af852008-02-12 20:43:05 +0000142 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100143 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144 }
145 }
146
Simon Kelley3be34542004-09-11 19:12:13 +0100147 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
148 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000149 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100150 mess->ciaddr.s_addr = 0;
151
Simon Kelley316e2732010-01-22 20:16:09 +0000152 /* search for device identity from CPEWAN devices, we pass this through to the script */
153 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
154 {
155 unsigned int elen, offset, len = option_len(opt);
156
157 for (offset = 0; offset < (len - 5); offset += elen + 5)
158 {
159 elen = option_uint(opt, offset + 4 , 1);
Simon Kelley6a0b00f2017-09-25 20:19:55 +0100160 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
Simon Kelley316e2732010-01-22 20:16:09 +0000161 {
162 unsigned char *x = option_ptr(opt, offset + 5);
163 unsigned char *y = option_ptr(opt, offset + elen + 5);
164 oui = option_find1(x, y, 1, 1);
165 serial = option_find1(x, y, 2, 1);
Vladislav Grishenko99e88912013-11-26 11:02:29 +0000166#ifdef HAVE_SCRIPT
167 class = option_find1(x, y, 3, 1);
168#endif
Simon Kelley316e2732010-01-22 20:16:09 +0000169 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
170 the gateway id back. Note that the device class is optional */
171 if (oui && serial)
172 {
173 cpewan_id.net = "cpewan-id";
174 cpewan_id.next = netid;
175 netid = &cpewan_id;
176 }
177 break;
178 }
179 }
180 }
181
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100182 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
183 {
184 /* Any agent-id needs to be copied back out, verbatim, as the last option
185 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
186 get overwritten, then it will be shuffled back at the end of processing.
187 Note that the incoming options must not be overwritten here, so there has to
188 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100189 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100190 unsigned int total = option_len(opt) + 2;
Simon Kelley591ed1e2016-07-11 18:18:42 +0100191 unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz,
192 OPTION_END, 0);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100193 if (last_opt && last_opt < end - total)
194 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100195 end -= total;
196 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100197 memcpy(agent_id, opt, total);
198 }
199
200 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100201 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100202 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100203
204 /* look for RFC5107 server-identifier-override */
205 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
206 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100207
208 /* if a circuit-id or remote-is option is provided, exact-match to options. */
209 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
210 {
211 int search;
212
213 if (vendor->match_type == MATCH_CIRCUIT)
214 search = SUBOPT_CIRCUIT_ID;
215 else if (vendor->match_type == MATCH_REMOTE)
216 search = SUBOPT_REMOTE_ID;
217 else if (vendor->match_type == MATCH_SUBSCRIBER)
218 search = SUBOPT_SUBSCR_ID;
219 else
220 continue;
221
Simon Kelley1a6bca82008-07-11 11:11:42 +0100222 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100223 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100224 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100225 {
226 vendor->netid.next = netid;
227 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100228 }
229 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100230 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100231
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100232 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
233 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100234 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000235
236 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000237 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000238 {
Simon Kelley26128d22004-11-14 16:43:54 +0000239 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100240 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000241 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000242
Simon Kelley0a852542005-03-23 20:28:59 +0000243 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100244 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000245
246 /* If this request is missing a clid, but we've seen one before,
247 use it again for option matching etc. */
248 if (lease && !clid && lease->clid)
249 {
250 clid_len = lease->clid_len;
251 clid = lease->clid;
252 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000253
254 /* find mac to use for logging and hashing */
255 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000256 }
Simon Kelley3be34542004-09-11 19:12:13 +0100257
Simon Kelleycdeda282006-03-16 20:16:06 +0000258 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
259 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100260 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
261 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000262 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263 mac->netid.next = netid;
264 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000265 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100266
Simon Kelley0a852542005-03-23 20:28:59 +0000267 /* Determine network for this packet. Our caller will have already linked all the
268 contexts which match the addresses of the receiving interface but if the
269 machine has an address already, or came via a relay, or we have a subnet selector,
270 we search again. If we don't have have a giaddr or explicit subnet selector,
271 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100272 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
273 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000274 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
275 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100276 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100277 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100278 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000279
Simon Kelley3d8df262005-08-29 12:19:27 +0100280 if (subnet_addr.s_addr)
281 {
282 addr = subnet_addr;
283 force = 1;
284 }
285 else if (mess->giaddr.s_addr)
286 {
287 addr = mess->giaddr;
288 force = 1;
289 }
Simon Kelley16972692006-10-16 20:04:18 +0100290 else
291 {
292 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
293 addr = mess->ciaddr;
294 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
295 if (context_tmp->netmask.s_addr &&
296 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
297 is_same_net(addr, context_tmp->end, context_tmp->netmask))
298 {
299 context_new = context;
300 break;
301 }
302 }
303
304 if (!context_new)
305 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100306 {
307 struct in_addr netmask = context_tmp->netmask;
308
309 /* guess the netmask for relayed networks */
310 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
311 {
312 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
313 netmask.s_addr = htonl(0xff000000);
314 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
315 netmask.s_addr = htonl(0xffff0000);
316 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
317 netmask.s_addr = htonl(0xffffff00);
318 }
319
320 /* This section fills in context mainly when a client which is on a remote (relayed)
321 network renews a lease without using the relay, after dnsmasq has restarted. */
322 if (netmask.s_addr != 0 &&
323 is_same_net(addr, context_tmp->start, netmask) &&
324 is_same_net(addr, context_tmp->end, netmask))
325 {
326 context_tmp->netmask = netmask;
327 if (context_tmp->local.s_addr == 0)
328 context_tmp->local = fallback;
329 if (context_tmp->router.s_addr == 0)
330 context_tmp->router = mess->giaddr;
331
332 /* fill in missing broadcast addresses for relayed ranges */
333 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
334 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
335
336 context_tmp->current = context_new;
337 context_new = context_tmp;
338 }
339 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100340
Simon Kelley3d8df262005-08-29 12:19:27 +0100341 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100342 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000343 }
Simon Kelley3be34542004-09-11 19:12:13 +0100344
345 if (!context)
346 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100347 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100348 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
349 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
Simon Kelley3be34542004-09-11 19:12:13 +0100350 return 0;
351 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100352
Simon Kelley28866e92011-02-14 20:19:14 +0000353 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100354 {
355 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100356 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
357 {
358 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100359 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100360 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100361 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100362 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100363 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100364 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100365 }
366 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100367
368 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
369 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000370 If no data given, existence of the option is enough. This code handles
Simon Kelley86e92f92013-04-23 11:31:39 +0100371 rfc3925 V-I classes too. */
372 for (o = daemon->dhcp_match; o; o = o->next)
373 {
374 unsigned int len, elen, match = 0;
375 size_t offset, o2;
376
377 if (o->flags & DHOPT_RFC3925)
378 {
379 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
380 continue;
381
382 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
383 {
384 len = option_uint(opt, offset + 4 , 1);
385 /* Need to take care that bad data can't run us off the end of the packet */
Matthias Andree9828ab12017-05-21 22:41:16 +0100386 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100387 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
388 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
389 {
390 elen = option_uint(opt, o2, 1);
391 if ((o2 + elen + 1 <= option_len(opt)) &&
392 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
393 break;
394 }
395 if (match)
396 break;
397 }
398 }
399 else
400 {
401 if (!(opt = option_find(mess, sz, o->opt, 1)))
402 continue;
403
404 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
405 }
406
407 if (match)
408 {
409 o->netid->next = netid;
410 netid = o->netid;
411 }
412 }
413
414 /* user-class options are, according to RFC3004, supposed to contain
415 a set of counted strings. Here we check that this is so (by seeing
416 if the counts are consistent with the overall option length) and if
417 so zero the counts so that we don't get spurious matches between
418 the vendor string and the counts. If the lengths don't add up, we
419 assume that the option is a single string and non RFC3004 compliant
420 and just do the substring match. dhclient provides these broken options.
421 The code, later, which sends user-class data to the lease-change script
422 relies on the transformation done here.
423 */
424
425 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
426 {
427 unsigned char *ucp = option_ptr(opt, 0);
428 int tmp, j;
429 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
430 if (j == option_len(opt))
431 for (j = 0; j < option_len(opt); j = tmp)
432 {
433 tmp = j + ucp[j] + 1;
434 ucp[j] = 0;
435 }
436 }
437
438 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
439 {
440 int mopt;
441
442 if (vendor->match_type == MATCH_VENDOR)
443 mopt = OPTION_VENDOR_ID;
444 else if (vendor->match_type == MATCH_USER)
445 mopt = OPTION_USER_CLASS;
446 else
447 continue;
448
449 if ((opt = option_find(mess, sz, mopt, 1)))
450 {
451 int i;
452 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
453 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
454 {
455 vendor->netid.next = netid;
456 netid = &vendor->netid;
457 break;
458 }
459 }
460 }
461
462 /* mark vendor-encapsulated options which match the client-supplied vendor class,
463 save client-supplied vendor class */
464 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
465 {
466 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
467 vendor_class_len = option_len(opt);
468 }
469 match_vendor_opts(opt, daemon->dhcp_opts);
470
471 if (option_bool(OPT_LOG_OPTS))
472 {
473 if (sanitise(opt, daemon->namebuff))
474 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
475 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
476 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
477 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100478
Simon Kelley3be34542004-09-11 19:12:13 +0100479 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100480
Simon Kelleycdeda282006-03-16 20:16:06 +0000481 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
482 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100483
484 /* set "known" tag for known hosts */
485 if (config)
486 {
487 known_id.net = "known";
488 known_id.next = netid;
489 netid = &known_id;
490 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100491 else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
492 mess->chaddr, mess->hlen, mess->htype, NULL))
493 {
494 known_id.net = "known-othernet";
495 known_id.next = netid;
496 netid = &known_id;
497 }
Simon Kelley26128d22004-11-14 16:43:54 +0000498
Simon Kelley316e2732010-01-22 20:16:09 +0000499 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100500 {
501 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000502 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000503 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100504
505 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100506 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100507 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000508
Simon Kelley26128d22004-11-14 16:43:54 +0000509 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000510 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000511
512 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
513
514 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000515 {
516 hostname = config->hostname;
517 domain = config->domain;
518 }
519
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100520 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000521 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100522 struct dhcp_netid_list *list;
523
524 for (list = config->netid; list; list = list->next)
525 {
526 list->list->next = netid;
527 netid = list->list;
528 }
Simon Kelley26128d22004-11-14 16:43:54 +0000529 }
530
531 /* Match incoming filename field as a netid. */
532 if (mess->file[0])
533 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000534 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
535 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
536 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000537 id.next = netid;
538 netid = &id;
539 }
Simon Kelley6b010842007-02-12 20:32:07 +0000540
541 /* Add "bootp" as a tag to allow different options, address ranges etc
542 for BOOTP clients */
543 bootp_id.net = "bootp";
544 bootp_id.next = netid;
545 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000546
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100547 tagif_netid = run_tag_if(netid);
548
Simon Kelley26128d22004-11-14 16:43:54 +0000549 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100550 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100551 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000552
Simon Kelley3d8df262005-08-29 12:19:27 +0100553 if (!message)
554 {
Simon Kelley9009d742008-11-14 20:04:27 +0000555 int nailed = 0;
556
Simon Kelley3d8df262005-08-29 12:19:27 +0100557 if (have_config(config, CONFIG_ADDR))
558 {
Simon Kelley9009d742008-11-14 20:04:27 +0000559 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100560 logaddr = &config->addr;
561 mess->yiaddr = config->addr;
562 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000563 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100564 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000565 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000566 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100567 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100568 else
569 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000570 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100571 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000572 {
573 if (lease)
574 {
575 /* lease exists, wrong network. */
576 lease_prune(lease, now);
577 lease = NULL;
578 }
Simon Kelleyc7be0162017-05-10 22:21:53 +0100579 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
Simon Kelleye17fb622006-01-14 20:33:46 +0000580 message = _("no address available");
581 }
582 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100583 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100584 }
585
Simon Kelley9009d742008-11-14 20:04:27 +0000586 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
587 message = _("wrong network");
588 else if (context->netid.net)
589 {
590 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100591 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100592 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100593
Simon Kelley4cb1b322012-02-06 14:30:41 +0000594 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100595
Simon Kelley9009d742008-11-14 20:04:27 +0000596 if (!message && !nailed)
597 {
598 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100599 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000600 break;
601 if (!id_list)
602 message = _("no address configured");
603 }
604
Simon Kelley7cebd202006-05-06 14:13:33 +0100605 if (!message &&
606 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000607 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000608 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000609
Simon Kelley3d8df262005-08-29 12:19:27 +0100610 if (!message)
611 {
612 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100613
Simon Kelleya9ab7322012-04-28 11:29:37 +0100614 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100615 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000616 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100617 /* infinite lease unless nailed in dhcp-host line. */
618 lease_set_expires(lease,
619 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
620 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000621 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100622
Simon Kelleya9df0e32017-04-28 22:43:00 +0100623 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000624 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +0100625 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100626 }
627 }
628
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100629 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000630
Simon Kelley7de060b2011-08-26 17:24:52 +0100631 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100632 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000633
Roy Marples3f3adae2013-07-25 16:22:46 +0100634 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100635 {
636 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
637 int len = option_len(opt);
638 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100639 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100640
Simon Kelley9fed0f72012-08-30 11:43:35 +0100641 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100642 len -= 3;
643 op += 3;
644 pp = op;
645
Simon Kelley9fed0f72012-08-30 11:43:35 +0100646 /* NB, the following always sets at least one bit */
647 if (option_bool(OPT_FQDN_UPDATE))
648 {
649 if (fqdn_flags & 0x01)
650 {
651 fqdn_flags |= 0x02; /* set O */
652 fqdn_flags &= ~0x01; /* clear S */
653 }
654 fqdn_flags |= 0x08; /* set N */
655 }
656 else
657 {
658 if (!(fqdn_flags & 0x01))
659 fqdn_flags |= 0x03; /* set S and O */
660 fqdn_flags &= ~0x08; /* clear N */
661 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100662
663 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100664 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100665 {
666 memcpy(pq, op+1, *op);
667 pq += *op;
668 op += (*op)+1;
669 *(pq++) = '.';
670 }
671 else
672 {
673 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000674 if (len > 0 && op[len-1] == 0)
675 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100676 pq += len + 1;
677 }
678
679 if (pq != daemon->dhcp_buff)
680 pq--;
681
682 *pq = 0;
683
Simon Kelley1f15b812009-10-13 17:49:32 +0100684 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100685 offer_hostname = client_hostname = daemon->dhcp_buff;
686 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000687 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000688 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000689 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100690 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000691 /* Microsoft clients are broken, and need zero-terminated strings
692 in options. We detect this state here, and do the same in
693 any options we send */
694 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
695 borken_opt = 1;
696 else
697 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100698 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100699 client_hostname = daemon->dhcp_buff;
700 }
701
Simon Kelley28866e92011-02-14 20:19:14 +0000702 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100703 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
704
Simon Kelley3d8df262005-08-29 12:19:27 +0100705 if (have_config(config, CONFIG_NAME))
706 {
707 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000708 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000709 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000710 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100711 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000712 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100713 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100714 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100715 {
Simon Kelley9009d742008-11-14 20:04:27 +0000716 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100717
718 if (strlen(client_hostname) != 0)
719 {
720 hostname = client_hostname;
721 if (!config)
722 {
723 /* Search again now we have a hostname.
724 Only accept configs without CLID and HWADDR here, (they won't match)
725 to avoid impersonation by name. */
726 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
727 mess->chaddr, mess->hlen,
728 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000729 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000730 {
731 config = new;
732 /* set "known" tag for known hosts */
733 known_id.net = "known";
734 known_id.next = netid;
735 netid = &known_id;
736 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100737 }
738 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000739 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100740
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100741 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100742 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100743 struct dhcp_netid_list *list;
744
745 for (list = config->netid; list; list = list->next)
746 {
747 list->list->next = netid;
748 netid = list->list;
749 }
Simon Kelleya2226412004-05-13 20:27:08 +0100750 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100751
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100752 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100753
Simon Kelley26128d22004-11-14 16:43:54 +0000754 /* if all the netids in the ignore list are present, ignore this client */
755 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100756 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000757 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100758
759 /* If configured, we can override the server-id to be the address of the relay,
760 so that all traffic goes via the relay and can pick up agent-id info. This can be
761 configured for all relays, or by address. */
762 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
763 {
764 if (!daemon->override_relays)
765 override = mess->giaddr;
766 else
767 {
768 struct addr_list *l;
769 for (l = daemon->override_relays; l; l = l->next)
770 if (l->addr.s_addr == mess->giaddr.s_addr)
771 break;
772 if (l)
773 override = mess->giaddr;
774 }
775 }
776
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100777 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
778 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000779 clid = NULL;
780
Simon Kelley7622fc02009-06-04 20:32:05 +0100781 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100782 if (daemon->enable_pxe &&
783 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100784 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
785 {
786 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
787 {
788 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
789 uuid = pxe_uuid;
790 }
791
792 /* Check if this is really a PXE bootserver request, and handle specially if so. */
793 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
794 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
795 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
796 {
797 struct pxe_service *service;
798 int type = option_uint(opt, 0, 2);
799 int layer = option_uint(opt, 2, 2);
800 unsigned char save71[4];
801 struct dhcp_opt opt71;
802
Simon Kelley1f15b812009-10-13 17:49:32 +0100803 if (ignore)
804 return 0;
805
Simon Kelley7622fc02009-06-04 20:32:05 +0100806 if (layer & 0x8000)
807 {
808 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
809 return 0;
810 }
811
812 memcpy(save71, option_ptr(opt, 0), 4);
813
814 for (service = daemon->pxe_services; service; service = service->next)
815 if (service->type == type)
816 break;
817
Simon Kelley549b1a42015-05-20 20:20:24 +0100818 for (; context; context = context->current)
819 if (match_netid(context->filter, tagif_netid, 1) &&
820 is_same_net(mess->ciaddr, context->start, context->netmask))
821 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100822
Simon Kelley549b1a42015-05-20 20:20:24 +0100823 if (!service || !service->basename || !context)
824 return 0;
825
Simon Kelleya9df0e32017-04-28 22:43:00 +0100826 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100827
828 mess->yiaddr = mess->ciaddr;
829 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000830 if (service->sname)
831 mess->siaddr = a_record_from_hosts(service->sname, now);
832 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 mess->siaddr = service->server;
834 else
835 mess->siaddr = context->local;
836
Matthias Andreef77700a2017-05-21 22:36:09 +0100837 if (strchr(service->basename, '.'))
838 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100839 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100840 else
841 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100842 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100843
Simon Kelley7622fc02009-06-04 20:32:05 +0100844 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
845 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
846 pxe_misc(mess, end, uuid);
847
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100848 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100849 opt71.val = save71;
850 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
851 opt71.len = 4;
852 opt71.flags = DHOPT_VENDOR_MATCH;
853 opt71.netid = NULL;
854 opt71.next = daemon->dhcp_opts;
855 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
856
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100857 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000858 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100859 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100860 }
861
862 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
863 {
864 pxearch = option_uint(opt, 0, 2);
865
Simon Kelley316e2732010-01-22 20:16:09 +0000866 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000867 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100868 {
Simon Kelley28866e92011-02-14 20:19:14 +0000869 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100870 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100871
Simon Kelley28866e92011-02-14 20:19:14 +0000872 for (tmp = context; tmp; tmp = tmp->current)
873 if ((tmp->flags & CONTEXT_PROXY) &&
874 match_netid(tmp->filter, tagif_netid, 1))
875 break;
876
877 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100878 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100879 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100880 int redirect4011 = 0;
881
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100882 if (tmp->netid.net)
883 {
884 tmp->netid.next = netid;
885 tagif_netid = run_tag_if(&tmp->netid);
886 }
887
888 boot = find_boot(tagif_netid);
889
Simon Kelley28866e92011-02-14 20:19:14 +0000890 mess->yiaddr.s_addr = 0;
891 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
892 {
893 mess->ciaddr.s_addr = 0;
894 mess->flags |= htons(0x8000); /* broadcast */
895 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100896
Simon Kelleya9df0e32017-04-28 22:43:00 +0100897 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000898
Simon Kelley0a4a0492016-05-15 20:13:45 +0100899 /* Redirect EFI clients to port 4011 */
900 if (pxearch >= 6)
901 {
902 redirect4011 = 1;
903 mess->siaddr = tmp->local;
904 }
905
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100906 /* Returns true if only one matching service is available. On port 4011,
907 it also inserts the boot file and server name. */
908 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100909
910 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000911 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000912 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100913 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100914 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000915 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100916 else if (boot->tftp_sname)
917 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000918
919 if (boot->file)
920 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
921 }
922
923 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
924 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100925 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Simon Kelley28866e92011-02-14 20:19:14 +0000926 pxe_misc(mess, end, uuid);
927 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +0100928 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +0100929 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
930
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100931 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000932 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +0100933 if (!ignore)
934 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100935 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100936 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100937 }
938 }
939 }
940
941 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000942 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100943 return 0;
944
Simon Kelleybb01cb92004-12-13 20:56:23 +0000945 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100946 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100947 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100948 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000949 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100950 }
951
Simon Kelley3be34542004-09-11 19:12:13 +0100952 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000953 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000954 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000955 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000956 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000957 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100958
Simon Kelley44a2a312004-03-10 20:04:35 +0000959 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100960 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000961
Simon Kelleybb01cb92004-12-13 20:56:23 +0000962 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000963 return 0;
964
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100965 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 +0000966
967 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
968 lease_prune(lease, now);
969
Simon Kelley33820b72004-04-03 21:10:00 +0100970 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000971 config->addr.s_addr == option_addr(opt).s_addr)
972 {
Simon Kelley849a8352006-06-09 21:02:31 +0100973 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100974 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100975 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100976 config->flags |= CONFIG_DECLINED;
977 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000978 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100979 else
980 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100981 for (; context; context = context->current)
982 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000983
984 return 0;
985
986 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100987 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100988 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000989 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000990 return 0;
991
Simon Kelley44a2a312004-03-10 20:04:35 +0000992 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
993 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100994 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000995 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100996
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100997 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000998
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000999 return 0;
1000
1001 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001002 if (ignore || have_config(config, CONFIG_DISABLE))
1003 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001004 if (option_bool(OPT_QUIET_DHCP))
1005 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001006 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001007 opt = NULL;
1008 }
1009 else
1010 {
1011 struct in_addr addr, conf;
1012
Simon Kelley1a6bca82008-07-11 11:11:42 +01001013 addr.s_addr = conf.s_addr = 0;
1014
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001015 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1016 addr = option_addr(opt);
1017
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001018 if (have_config(config, CONFIG_ADDR))
1019 {
Simon Kelley849a8352006-06-09 21:02:31 +01001020 char *addrs = inet_ntoa(config->addr);
1021
Simon Kelley9009d742008-11-14 20:04:27 +00001022 if ((ltmp = lease_find_by_addr(config->addr)) &&
1023 ltmp != lease &&
1024 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001025 {
1026 int len;
1027 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1028 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001029 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +01001030 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001031 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001032 else
1033 {
1034 struct dhcp_context *tmp;
1035 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001036 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001037 break;
1038 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001039 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 +01001040 else if (have_config(config, CONFIG_DECLINED) &&
1041 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001042 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001043 else
1044 conf = config->addr;
1045 }
1046 }
1047
1048 if (conf.s_addr)
1049 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001050 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001051 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001052 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001053 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001054 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001055 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001056 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001057 else if (emac_len == 0)
1058 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001059 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001060 message = _("no address available");
1061 }
1062
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001063 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 +01001064
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001065 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001066 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001067
Simon Kelleycdeda282006-03-16 20:16:06 +00001068 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001069 {
1070 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001071 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001072 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001073
Simon Kelley4cb1b322012-02-06 14:30:41 +00001074 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001075 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001076
1077 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1078 {
1079 rapid_commit = 1;
1080 goto rapid_commit;
1081 }
1082
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001083 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001084
Simon Kelley824af852008-02-12 20:43:05 +00001085 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001086 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001087 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001088 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001089 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001090 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001091 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001092 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001093
Simon Kelley7de060b2011-08-26 17:24:52 +01001094 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001095
1096
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001097 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001098 if (ignore || have_config(config, CONFIG_DISABLE))
1099 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001100 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001101 {
1102 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001103 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001104
Simon Kelley4011c4e2006-10-28 16:26:19 +01001105 /* send vendor and user class info for new or recreated lease */
1106 do_classes = 1;
1107
Simon Kelleybb01cb92004-12-13 20:56:23 +00001108 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001109 {
Simon Kelley3be34542004-09-11 19:12:13 +01001110 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001111 selecting = 1;
1112
Simon Kelley1a6bca82008-07-11 11:11:42 +01001113 if (override.s_addr != 0)
1114 {
1115 if (option_addr(opt).s_addr != override.s_addr)
1116 return 0;
1117 }
Simon Kelley9009d742008-11-14 20:04:27 +00001118 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001119 {
1120 for (; context; context = context->current)
1121 if (context->local.s_addr == option_addr(opt).s_addr)
1122 break;
1123
1124 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001125 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001126 /* Handle very strange configs where clients have more than one route to the server.
1127 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1128 Have to set override to make sure we echo back the correct server-id */
1129 struct irec *intr;
1130
Simon Kelley115ac3e2013-05-20 11:28:32 +01001131 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001132
1133 for (intr = daemon->interfaces; intr; intr = intr->next)
1134 if (intr->addr.sa.sa_family == AF_INET &&
1135 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1136 intr->tftp_ok)
1137 break;
1138
1139 if (intr)
1140 override = intr->addr.in.sin_addr;
1141 else
1142 {
1143 /* In auth mode, a REQUEST sent to the wrong server
1144 should be faulted, so that the client establishes
1145 communication with us, otherwise, silently ignore. */
1146 if (!option_bool(OPT_AUTHORITATIVE))
1147 return 0;
1148 message = _("wrong server-ID");
1149 }
Simon Kelley9009d742008-11-14 20:04:27 +00001150 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001151 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001152
Simon Kelley3be34542004-09-11 19:12:13 +01001153 /* If a lease exists for this host and another address, squash it. */
1154 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1155 {
1156 lease_prune(lease, now);
1157 lease = NULL;
1158 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001159 }
Simon Kelley3be34542004-09-11 19:12:13 +01001160 else
1161 {
1162 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001163 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001164 return 0;
1165
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001166 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001167 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001168 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001169 }
1170 else
1171 {
1172 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001173 /* Check existing lease for this address.
1174 We allow it to be missing if dhcp-authoritative mode
1175 as long as we can allocate the lease now - checked below.
1176 This makes for a smooth recovery from a lost lease DB */
1177 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001178 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001179 {
Simon Kelley28866e92011-02-14 20:19:14 +00001180 /* A client rebinding will broadcast the request, so we may see it even
1181 if the lease is held by another server. Just ignore it in that case.
1182 If the request is unicast to us, then somethings wrong, NAK */
1183 if (!unicast_dest)
1184 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001185 message = _("lease not found");
1186 /* ensure we broadcast NAK */
1187 unicast_dest = 0;
1188 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001189
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001190 /* desynchronise renewals */
1191 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001192 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001193 }
Simon Kelley734d5312018-03-23 23:09:53 +00001194
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001195 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001196
1197 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001198 if (!message)
1199 {
1200 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001201 struct dhcp_context *tmp = NULL;
1202
1203 if (have_config(config, CONFIG_ADDR))
1204 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001205 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001206 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001207
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001208 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001209 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001210 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001211 message = _("wrong network");
1212 /* ensure we broadcast NAK */
1213 unicast_dest = 0;
1214 }
1215
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001216 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001217 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001218 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001219 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001220
Simon Kelleydfa666f2004-08-02 18:27:27 +01001221 /* Check if a new static address has been configured. Be very sure that
1222 when the client does DISCOVER, it will get the static address, otherwise
1223 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001224 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001225 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001226 (!have_config(config, CONFIG_DECLINED) ||
1227 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001228 config->addr.s_addr != mess->yiaddr.s_addr &&
1229 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001230 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001231
1232 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001233 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001234 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001235
Simon Kelley9009d742008-11-14 20:04:27 +00001236 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1237 {
1238 /* If a host is configured with more than one MAC address, it's OK to 'nix
1239 a lease from one of it's MACs to give the address to another. */
1240 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1241 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001242 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001243 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1244 inet_ntoa(ltmp->addr));
1245 lease = ltmp;
1246 }
Simon Kelley16972692006-10-16 20:04:18 +01001247 else
Simon Kelley9009d742008-11-14 20:04:27 +00001248 message = _("address in use");
1249 }
1250
1251 if (!message)
1252 {
1253 if (emac_len == 0)
1254 message = _("no unique-id");
1255
1256 else if (!lease)
1257 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001258 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001259 do_classes = 1;
1260 else
1261 message = _("no leases left");
1262 }
Simon Kelley16972692006-10-16 20:04:18 +01001263 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001264 }
Simon Kelley16972692006-10-16 20:04:18 +01001265
Simon Kelley44a2a312004-03-10 20:04:35 +00001266 if (message)
1267 {
Simon Kelley734d5312018-03-23 23:09:53 +00001268 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1269
1270 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1271 if (rapid_commit)
1272 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001273
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001274 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001275 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001276 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001277 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001278 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001279 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1280 a distant subnet which unicast a REQ to us won't work. */
1281 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1282 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1283 {
1284 mess->flags |= htons(0x8000); /* broadcast */
1285 mess->ciaddr.s_addr = 0;
1286 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001287 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001288 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001289 {
Simon Kelley316e2732010-01-22 20:16:09 +00001290 if (context->netid.net)
1291 {
1292 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001293 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001294 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001295
Simon Kelley4cb1b322012-02-06 14:30:41 +00001296 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001297
Simon Kelleydcffad22012-04-24 15:25:18 +01001298 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001299 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001300 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001301 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001302
Simon Kelleydcffad22012-04-24 15:25:18 +01001303#ifdef HAVE_SCRIPT
1304 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001305 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001306 struct dhcp_netid *n;
1307
1308 if (mess->giaddr.s_addr)
1309 lease->giaddr = mess->giaddr;
1310
1311 free(lease->extradata);
1312 lease->extradata = NULL;
1313 lease->extradata_size = lease->extradata_len = 0;
1314
1315 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1316 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1317 add_extradata_opt(lease, oui);
1318 add_extradata_opt(lease, serial);
1319 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001320
1321 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1322 {
1323 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1324 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1325 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1326 }
1327 else
1328 {
1329 add_extradata_opt(lease, NULL);
1330 add_extradata_opt(lease, NULL);
1331 add_extradata_opt(lease, NULL);
1332 }
1333
ZHAO Yuf89cae32016-12-22 22:32:31 +00001334 /* DNSMASQ_REQUESTED_OPTIONS */
1335 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1336 {
1337 int len = option_len(opt);
1338 unsigned char *rop = option_ptr(opt, 0);
1339 char *q = daemon->namebuff;
1340 int i;
1341 for (i = 0; i < len; i++)
1342 {
1343 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1344 }
1345 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1346 }
1347 else
1348 {
1349 add_extradata_opt(lease, NULL);
1350 }
1351
Simon Kelleydcffad22012-04-24 15:25:18 +01001352 /* space-concat tag set */
1353 if (!tagif_netid)
1354 add_extradata_opt(lease, NULL);
1355 else
1356 for (n = tagif_netid; n; n = n->next)
1357 {
1358 struct dhcp_netid *n1;
1359 /* kill dupes */
1360 for (n1 = n->next; n1; n1 = n1->next)
1361 if (strcmp(n->net, n1->net) == 0)
1362 break;
1363 if (!n1)
1364 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1365 }
1366
1367 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1368 {
1369 int len = option_len(opt);
1370 unsigned char *ucp = option_ptr(opt, 0);
1371 /* If the user-class option started as counted strings, the first byte will be zero. */
1372 if (len != 0 && ucp[0] == 0)
1373 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001374 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001375 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001376 }
Simon Kelley316e2732010-01-22 20:16:09 +00001377#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001378 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001379
1380 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1381 {
1382 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001383 hostname = client_hostname;
1384 hostname_auth = 1;
1385 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001386
Simon Kelley824af852008-02-12 20:43:05 +00001387 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001388 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001389
Simon Kelley832af0b2007-01-21 20:01:28 +00001390 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1391 if (!hostname_auth)
1392 {
1393 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001394 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001395 break;
1396 if (id_list)
1397 hostname = NULL;
1398 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001399
1400 /* Last ditch, if configured, generate hostname from mac address */
1401 if (!hostname && emac_len != 0)
1402 {
1403 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1404 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1405 break;
1406 if (id_list)
1407 {
1408 int i;
1409
1410 hostname = daemon->dhcp_buff;
1411 /* buffer is 256 bytes, 3 bytes per octet */
1412 for (i = 0; (i < emac_len) && (i < 80); i++)
1413 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1414 hostname = daemon->dhcp_buff;
1415 }
1416 }
1417
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001418 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001419 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001420
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001421 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001422 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001423
1424 if (override.s_addr != 0)
1425 lease->override = override;
1426 else
1427 override = lease->override;
1428
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001429 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001430
Simon Kelleya9df0e32017-04-28 22:43:00 +01001431 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001432 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001433 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001434 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001435 if (rapid_commit)
1436 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1437 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001438 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001439 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001440
Simon Kelley7de060b2011-08-26 17:24:52 +01001441 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001442
1443 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001444 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001445 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001446
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001447 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001448
Simon Kelley73a08a22009-02-05 20:28:08 +00001449 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001450 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001451
1452 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001453 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001454
Simon Kelley5aabfc72007-08-29 11:24:47 +01001455 /* Find a least based on IP address if we didn't
1456 get one from MAC address/client-d */
1457 if (!lease &&
1458 (lease = lease_find_by_addr(mess->ciaddr)) &&
1459 lease->hostname)
1460 hostname = lease->hostname;
1461
Simon Kelley0f371f92013-07-27 15:15:38 +01001462 if (!hostname)
1463 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001464
Simon Kelley73a08a22009-02-05 20:28:08 +00001465 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001466 {
1467 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001468 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001469 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001470
Simon Kelley4cb1b322012-02-06 14:30:41 +00001471 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001472
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001473 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001474
Simon Kelley3927da42008-07-20 15:10:39 +01001475 if (lease)
1476 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001477 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001478 if (override.s_addr != 0)
1479 lease->override = override;
1480 else
1481 override = lease->override;
1482 }
1483
Simon Kelleya9df0e32017-04-28 22:43:00 +01001484 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001485 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001486 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001487
1488 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1489 we supply a utility which makes DHCPINFORM requests to get this information.
1490 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1491 which won't be true for ordinary clients, but will be true for the
1492 dhcp_lease_time utility. */
1493 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1494 {
1495 if (lease->expires == 0)
1496 time = 0xffffffff;
1497 else
1498 time = (unsigned int)difftime(lease->expires, now);
1499 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1500 }
1501
Simon Kelley9009d742008-11-14 20:04:27 +00001502 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001503 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001504
Simon Kelley5aabfc72007-08-29 11:24:47 +01001505 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001506 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001507 }
1508
1509 return 0;
1510}
1511
Simon Kelley6b010842007-02-12 20:32:07 +00001512/* find a good value to use as MAC address for logging and address-allocation hashing.
1513 This is normally just the chaddr field from the DHCP packet,
1514 but eg Firewire will have hlen == 0 and use the client-id instead.
1515 This could be anything, but will normally be EUI64 for Firewire.
1516 We assume that if the first byte of the client-id equals the htype byte
1517 then the client-id is using the usual encoding and use the rest of the
1518 client-id: if not we can use the whole client-id. This should give
1519 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001520unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001521 int clid_len, unsigned char *clid, int *len_out)
1522{
1523 if (hwlen == 0 && clid && clid_len > 3)
1524 {
1525 if (clid[0] == hwtype)
1526 {
1527 *len_out = clid_len - 1 ;
1528 return clid + 1;
1529 }
1530
1531#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1532 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1533 {
1534 *len_out = clid_len - 1 ;
1535 return clid + 1;
1536 }
1537#endif
1538
1539 *len_out = clid_len;
1540 return clid;
1541 }
1542
1543 *len_out = hwlen;
1544 return hwaddr;
1545}
1546
Simon Kelley824af852008-02-12 20:43:05 +00001547static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001548{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001549 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001550
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001551 if (opt)
1552 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001553 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001554 if (req_time < 120 )
1555 req_time = 120; /* sanity */
1556 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1557 time = req_time;
1558 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001559
1560 return time;
1561}
1562
Simon Kelley73a08a22009-02-05 20:28:08 +00001563static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001564{
Simon Kelley73a08a22009-02-05 20:28:08 +00001565 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001566 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001567 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001568 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001569 else
1570 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001571}
1572
Simon Kelleyf2621c72007-04-29 19:47:21 +01001573static int sanitise(unsigned char *opt, char *buf)
1574{
1575 char *p;
1576 int i;
1577
1578 *buf = 0;
1579
1580 if (!opt)
1581 return 0;
1582
Simon Kelley1a6bca82008-07-11 11:11:42 +01001583 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001584
1585 for (i = option_len(opt); i > 0; i--)
1586 {
1587 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001588 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001589 *buf++ = c;
1590 }
1591 *buf = 0; /* add terminator */
1592
1593 return 1;
1594}
1595
Simon Kelley316e2732010-01-22 20:16:09 +00001596#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001597static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1598{
1599 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001600 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001601 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001602 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001603}
1604#endif
1605
Simon Kelley5aabfc72007-08-29 11:24:47 +01001606static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001607 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001608{
Simon Kelley16972692006-10-16 20:04:18 +01001609 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001610
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001611 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001612 return;
1613
Simon Kelley16972692006-10-16 20:04:18 +01001614 /* addr may be misaligned */
1615 if (addr)
1616 memcpy(&a, addr, sizeof(a));
1617
Simon Kelley7622fc02009-06-04 20:32:05 +01001618 print_mac(daemon->namebuff, ext_mac, mac_len);
1619
Simon Kelley28866e92011-02-14 20:19:14 +00001620 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001621 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001622 ntohl(xid),
1623 type,
1624 interface,
1625 addr ? inet_ntoa(a) : "",
1626 addr ? " " : "",
1627 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001628 string ? string : "",
1629 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001630 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001631 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001632 type,
1633 interface,
1634 addr ? inet_ntoa(a) : "",
1635 addr ? " " : "",
1636 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001637 string ? string : "",
1638 err ? err : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001639}
1640
Simon Kelley7622fc02009-06-04 20:32:05 +01001641static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001642{
1643 while (*start != OPTION_END)
1644 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001645 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 +01001646
Simon Kelley4cb1b322012-02-06 14:30:41 +00001647 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1648 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001649 start += start[1] + 2;
1650 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001651}
1652
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001653static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001654{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001655 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001656 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001657 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001658 return NULL;
1659 else if (*p == OPTION_END)
1660 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001661 else if (*p == OPTION_PAD)
1662 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001663 else
1664 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001665 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001666 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001667 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001668 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001669 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001670 return NULL; /* malformed packet */
1671 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001672 return p;
1673 p += opt_len + 2;
1674 }
1675 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001676}
1677
Simon Kelleycdeda282006-03-16 20:16:06 +00001678static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001679{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001680 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001681
Simon Kelley3be34542004-09-11 19:12:13 +01001682 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001683 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1684 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001685
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001686 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001687 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001688 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001689
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001690 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001691 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001692 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1693 return ret;
1694
1695 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001696 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001697 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1698 return ret;
1699
1700 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001701}
1702
Simon Kelley4cb1b322012-02-06 14:30:41 +00001703static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001704{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001705 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001706 /* struct in_addr is network byte order */
1707 struct in_addr ret;
1708
Simon Kelley4cb1b322012-02-06 14:30:41 +00001709 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001710
1711 return ret;
1712}
1713
Simon Kelley7622fc02009-06-04 20:32:05 +01001714static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001715{
1716 /* this worries about unaligned data and byte order */
1717 unsigned int ret = 0;
1718 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001719 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001720
1721 for (i = 0; i < size; i++)
1722 ret = (ret << 8) | *p++;
1723
1724 return ret;
1725}
1726
1727static unsigned char *dhcp_skip_opts(unsigned char *start)
1728{
1729 while (*start != 0)
1730 start += start[1] + 2;
1731 return start;
1732}
1733
1734/* only for use when building packet: doesn't check for bad data. */
1735static unsigned char *find_overload(struct dhcp_packet *mess)
1736{
1737 unsigned char *p = &mess->options[0] + sizeof(u32);
1738
1739 while (*p != 0)
1740 {
1741 if (*p == OPTION_OVERLOAD)
1742 return p;
1743 p += p[1] + 2;
1744 }
1745 return NULL;
1746}
1747
Simon Kelley7de060b2011-08-26 17:24:52 +01001748static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1749{
1750 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1751 unsigned char *overload;
1752 size_t ret;
1753
1754 /* move agent_id back down to the end of the packet */
1755 if (agent_id)
1756 {
1757 memmove(p, agent_id, real_end - agent_id);
1758 p += real_end - agent_id;
1759 memset(p, 0, real_end - p); /* in case of overlap */
1760 }
1761
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001762 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001763 overload = find_overload(mess);
1764
1765 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001766 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001767 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001768 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001769 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001770 }
Simon Kelley28866e92011-02-14 20:19:14 +00001771 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001772 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1773
1774 if (overload && (option_uint(overload, 0, 1) & 2))
1775 {
1776 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001777 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001778 log_options(mess->sname, mess->xid);
1779 }
Simon Kelley28866e92011-02-14 20:19:14 +00001780 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001781 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1782
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001783
1784 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001785
Simon Kelley28866e92011-02-14 20:19:14 +00001786 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001787 {
1788 if (mess->siaddr.s_addr != 0)
1789 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1790
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001791 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1792 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1793
Simon Kelley7622fc02009-06-04 20:32:05 +01001794 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1795 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001796
1797 ret = (size_t)(p - (unsigned char *)mess);
1798
1799 if (ret < MIN_PACKETSZ)
1800 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001801
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001802 return ret;
1803}
1804
1805static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1806{
1807 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1808
1809 if (p + len + 3 >= end)
1810 /* not enough space in options area, try and use overload, if poss */
1811 {
1812 unsigned char *overload;
1813
1814 if (!(overload = find_overload(mess)) &&
1815 (mess->file[0] == 0 || mess->sname[0] == 0))
1816 {
1817 /* attempt to overload fname and sname areas, we've reserved space for the
1818 overflow option previuously. */
1819 overload = p;
1820 *(p++) = OPTION_OVERLOAD;
1821 *(p++) = 1;
1822 }
1823
1824 p = NULL;
1825
1826 /* using filename field ? */
1827 if (overload)
1828 {
1829 if (mess->file[0] == 0)
1830 overload[2] |= 1;
1831
1832 if (overload[2] & 1)
1833 {
1834 p = dhcp_skip_opts(mess->file);
1835 if (p + len + 3 >= mess->file + sizeof(mess->file))
1836 p = NULL;
1837 }
1838
1839 if (!p)
1840 {
1841 /* try to bring sname into play (it may be already) */
1842 if (mess->sname[0] == 0)
1843 overload[2] |= 2;
1844
1845 if (overload[2] & 2)
1846 {
1847 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001848 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001849 p = NULL;
1850 }
1851 }
1852 }
1853
1854 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001855 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 +00001856 }
1857
1858 if (p)
1859 {
1860 *(p++) = opt;
1861 *(p++) = len;
1862 }
1863
1864 return p;
1865}
1866
1867static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1868{
1869 int i;
1870 unsigned char *p = free_space(mess, end, opt, len);
1871
1872 if (p)
1873 for (i = 0; i < len; i++)
1874 *(p++) = val >> (8 * (len - (i + 1)));
1875}
1876
1877static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1878 char *string, int null_term)
1879{
1880 unsigned char *p;
1881 size_t len = strlen(string);
1882
1883 if (null_term && len != 255)
1884 len++;
1885
1886 if ((p = free_space(mess, end, opt, len)))
1887 memcpy(p, string, len);
1888}
1889
1890/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001891static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001892{
1893 int len = opt->len;
1894
1895 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1896 len++;
1897
1898 if (p && len != 0)
1899 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001900 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001901 {
1902 int j;
1903 struct in_addr *a = (struct in_addr *)opt->val;
1904 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1905 {
1906 /* zero means "self" (but not in vendorclass options.) */
1907 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001908 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001909 else
1910 memcpy(p, a, INADDRSZ);
1911 p += INADDRSZ;
1912 }
1913 }
1914 else
Simon Kelley625ac282013-07-02 21:19:32 +01001915 /* empty string may be extended to "\0" by null_term */
1916 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001917 }
1918 return len;
1919}
Simon Kelley7622fc02009-06-04 20:32:05 +01001920
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001921static int in_list(unsigned char *list, int opt)
1922{
1923 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001924
1925 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001926 if (!list)
1927 return 1;
1928
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001929 for (i = 0; list[i] != OPTION_END; i++)
1930 if (opt == list[i])
1931 return 1;
1932
1933 return 0;
1934}
1935
Simon Kelley7de060b2011-08-26 17:24:52 +01001936static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001937{
Simon Kelley7de060b2011-08-26 17:24:52 +01001938 struct dhcp_opt *opts;
1939
1940 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1941 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1942 return opts;
1943
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001944 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001945}
1946
Simon Kelley6b010842007-02-12 20:32:07 +00001947/* mark vendor-encapsulated options which match the client-supplied or
1948 config-supplied vendor class */
1949static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1950{
1951 for (; dopt; dopt = dopt->next)
1952 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001953 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001954 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001955 {
1956 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001957 if (dopt->u.vendor_class)
1958 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001959 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001960 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001961 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001962 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001963 break;
1964 }
1965 }
1966 }
1967}
1968
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001969static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1970 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001971{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001972 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001973 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001974 unsigned char *p;
1975
1976 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001977 for (enc_len = 0, start = opt; opt; opt = opt->next)
1978 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001979 {
1980 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001981 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001982 if (enc_len + new <= 255)
1983 enc_len += new;
1984 else
1985 {
1986 p = free_space(mess, end, encap, enc_len);
1987 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001988 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001989 {
1990 len = do_opt(start, p + 2, NULL, null_term);
1991 *(p++) = start->opt;
1992 *(p++) = len;
1993 p += len;
1994 }
1995 enc_len = new;
1996 start = opt;
1997 }
1998 }
1999
2000 if (enc_len != 0 &&
2001 (p = free_space(mess, end, encap, enc_len + 1)))
2002 {
2003 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002004 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002005 {
2006 len = do_opt(start, p + 2, NULL, null_term);
2007 *(p++) = start->opt;
2008 *(p++) = len;
2009 p += len;
2010 }
2011 *p = OPTION_END;
2012 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002013
2014 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002015}
2016
Simon Kelley7622fc02009-06-04 20:32:05 +01002017static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002018{
Simon Kelley7622fc02009-06-04 20:32:05 +01002019 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002020
Simon Kelley7622fc02009-06-04 20:32:05 +01002021 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2022 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2023 memcpy(p, uuid, 17);
2024}
2025
2026static int prune_vendor_opts(struct dhcp_netid *netid)
2027{
2028 int force = 0;
2029 struct dhcp_opt *opt;
2030
2031 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2032 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2033 if (opt->flags & DHOPT_VENDOR_MATCH)
2034 {
2035 if (!match_netid(opt->netid, netid, 1))
2036 opt->flags &= ~DHOPT_VENDOR_MATCH;
2037 else if (opt->flags & DHOPT_FORCE)
2038 force = 1;
2039 }
2040 return force;
2041}
2042
Simon Kelley8628cd62016-05-10 17:31:48 +01002043
2044/* Many UEFI PXE implementations have badly broken menu code.
2045 If there's exactly one relevant menu item, we abandon the menu system,
2046 and jamb the data direct into the DHCP file, siaddr and sname fields.
2047 Note that in this case, we have to assume that layer zero would be requested
2048 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002049static 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 +01002050{
2051 struct pxe_service *service, *found;
2052
2053 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002054 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002055 return 0;
2056
2057 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002058 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002059 {
2060 if (found)
2061 return 0; /* More than one relevant menu item */
2062
2063 found = service;
2064 }
2065
2066 if (!found)
2067 return 0; /* No relevant menu items. */
2068
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002069 if (!pxe)
2070 return 1;
2071
Simon Kelley8628cd62016-05-10 17:31:48 +01002072 if (found->sname)
2073 {
2074 mess->siaddr = a_record_from_hosts(found->sname, now);
2075 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2076 }
2077 else
2078 {
2079 if (found->server.s_addr != 0)
2080 mess->siaddr = found->server;
2081 else
2082 mess->siaddr = local;
2083
2084 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2085 }
2086
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002087 snprintf((char *)mess->file, sizeof(mess->file),
2088 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2089
Simon Kelley8628cd62016-05-10 17:31:48 +01002090 return 1;
2091}
2092
Simon Kelley751d6f42012-02-10 15:24:51 +00002093static 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 +01002094{
2095#define NUM_OPTS 4
2096
2097 unsigned char *p, *q;
2098 struct pxe_service *service;
2099 static struct dhcp_opt *o, *ret;
2100 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002101 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002102
2103 /* We pass back references to these, hence they are declared static */
2104 static unsigned char discovery_control;
2105 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2106 static struct dhcp_opt *fake_opts = NULL;
2107
Simon Kelley316e2732010-01-22 20:16:09 +00002108 /* Disable multicast, since we don't support it, and broadcast
2109 unless we need it */
2110 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002111
2112 ret = daemon->dhcp_opts;
2113
2114 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2115 return ret;
2116
2117 for (i = 0; i < NUM_OPTS; i++)
2118 {
2119 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2120 fake_opts[i].netid = NULL;
2121 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2122 }
2123
2124 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2125 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002126 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002127
2128 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2129 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2130 {
2131 size_t len = strlen(service->menu);
2132 /* opt 43 max size is 255. encapsulated option has type and length
2133 bytes, so its max size is 253. */
2134 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2135 {
2136 *(p++) = service->type >> 8;
2137 *(p++) = service->type;
2138 *(p++) = len;
2139 memcpy(p, service->menu, len);
2140 p += len;
2141 i++;
2142 }
2143 else
2144 {
2145 toobig:
2146 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2147 return daemon->dhcp_opts;
2148 }
2149
Simon Kelley751d6f42012-02-10 15:24:51 +00002150 boot_server = service->basename ? local :
2151 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2152
Simon Kelley316e2732010-01-22 20:16:09 +00002153 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002154 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002155 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002156 goto toobig;
2157
2158 /* Boot service with known address - give it */
2159 *(q++) = service->type >> 8;
2160 *(q++) = service->type;
2161 *(q++) = 1;
2162 /* dest misaligned */
2163 memcpy(q, &boot_server.s_addr, INADDRSZ);
2164 q += INADDRSZ;
2165 }
2166 else if (service->type != 0)
2167 /* We don't know the server for a service type, so we'll
2168 allow the client to broadcast for it */
2169 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002170 }
2171
2172 /* if no prompt, wait forever if there's a choice */
2173 fake_prompt[0] = (i > 1) ? 255 : 0;
2174
2175 if (i == 0)
2176 discovery_control = 8; /* no menu - just use use mess->filename */
2177 else
2178 {
2179 ret = &fake_opts[j--];
2180 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2181 ret->val = (unsigned char *)daemon->dhcp_buff;
2182 ret->opt = SUBOPT_PXE_MENU;
2183
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002184 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002185 {
2186 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002187 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2188 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002189 ret->opt = SUBOPT_PXE_SERVERS;
2190 }
2191 }
2192
2193 for (o = daemon->dhcp_opts; o; o = o->next)
2194 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2195 break;
2196
2197 if (!o)
2198 {
2199 ret = &fake_opts[j--];
2200 ret->len = sizeof(fake_prompt);
2201 ret->val = fake_prompt;
2202 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2203 }
2204
Simon Kelley316e2732010-01-22 20:16:09 +00002205 ret = &fake_opts[j--];
2206 ret->len = 1;
2207 ret->opt = SUBOPT_PXE_DISCOVERY;
2208 ret->val= &discovery_control;
2209
Simon Kelley7622fc02009-06-04 20:32:05 +01002210 return ret;
2211}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002212
2213static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002214{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002215 memset(mess->sname, 0, sizeof(mess->sname));
2216 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002217 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002218 mess->siaddr.s_addr = 0;
2219}
Simon Kelleycdeda282006-03-16 20:16:06 +00002220
Simon Kelley7622fc02009-06-04 20:32:05 +01002221struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002222{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002223 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002224
2225 /* decide which dhcp-boot option we're using */
2226 for (boot = daemon->boot_config; boot; boot = boot->next)
2227 if (match_netid(boot->netid, netid, 0))
2228 break;
2229 if (!boot)
2230 /* No match, look for one without a netid */
2231 for (boot = daemon->boot_config; boot; boot = boot->next)
2232 if (match_netid(boot->netid, netid, 1))
2233 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002234
2235 return boot;
2236}
2237
2238static void do_options(struct dhcp_context *context,
2239 struct dhcp_packet *mess,
2240 unsigned char *end,
2241 unsigned char *req_options,
2242 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002243 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002244 struct dhcp_netid *netid,
2245 struct in_addr subnet_addr,
2246 unsigned char fqdn_flags,
2247 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002248 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002249 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002250 time_t now,
2251 unsigned int lease_time,
2252 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002253{
2254 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2255 struct dhcp_boot *boot;
2256 unsigned char *p;
2257 int i, len, force_encap = 0;
2258 unsigned char f0 = 0, s0 = 0;
2259 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002260 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002261 struct dhcp_netid *tagif;
2262 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002263
Simon Kelley4cb1b322012-02-06 14:30:41 +00002264 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002265 if (context)
2266 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002267 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002268
Simon Kelley7622fc02009-06-04 20:32:05 +01002269 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002270 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002271 {
2272 char *q = daemon->namebuff;
2273 for (i = 0; req_options[i] != OPTION_END; i++)
2274 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002275 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002276 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2277 "%d%s%s%s",
2278 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002279 strlen(s) != 0 ? ":" : "",
2280 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002281 req_options[i+1] == OPTION_END ? "" : ", ");
2282 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2283 {
2284 q = daemon->namebuff;
2285 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2286 }
2287 }
2288 }
2289
Simon Kelley7de060b2011-08-26 17:24:52 +01002290 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2291 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2292 break;
2293 if (id_list)
2294 mess->flags |= htons(0x8000); /* force broadcast */
2295
Simon Kelley73a08a22009-02-05 20:28:08 +00002296 if (context)
2297 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002298
2299 /* See if we can send the boot stuff as options.
2300 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002301 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002302 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002303 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002304 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002305 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002306 {
2307 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002308 {
Simon Kelley28866e92011-02-14 20:19:14 +00002309 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002310 req_options &&
2311 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002312 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2313 else
Simon Kelley824af852008-02-12 20:43:05 +00002314 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002315 }
2316
2317 if (boot->file)
2318 {
Simon Kelley28866e92011-02-14 20:19:14 +00002319 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002320 req_options &&
2321 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002322 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2323 else
Simon Kelley824af852008-02-12 20:43:05 +00002324 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002325 }
2326
Simon Kelley7de060b2011-08-26 17:24:52 +01002327 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002328 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002329 else if (boot->tftp_sname)
2330 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002331 }
Simon Kelley824af852008-02-12 20:43:05 +00002332 else
2333 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002334 they're not explicitly asked for as options. OPTION_END is used
2335 as an internal way to specify siaddr without using dhcp-boot, for use in
2336 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002337 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002338 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002339 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002340 {
2341 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2342 done_file = 1;
2343 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002344
Simon Kelley824af852008-02-12 20:43:05 +00002345 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002346 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002347 {
2348 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2349 done_server = 1;
2350 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002351
Simon Kelley7de060b2011-08-26 17:24:52 +01002352 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002353 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002354 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002355
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002356 /* We don't want to do option-overload for BOOTP, so make the file and sname
2357 fields look like they are in use, even when they aren't. This gets restored
2358 at the end of this function. */
2359
Simon Kelley28866e92011-02-14 20:19:14 +00002360 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002361 {
2362 f0 = mess->file[0];
2363 mess->file[0] = 1;
2364 s0 = mess->sname[0];
2365 mess->sname[0] = 1;
2366 }
2367
2368 /* At this point, if mess->sname or mess->file are zeroed, they are available
2369 for option overload, reserve space for the overload option. */
2370 if (mess->file[0] == 0 || mess->sname[0] == 0)
2371 end -= 3;
2372
Simon Kelley3be34542004-09-11 19:12:13 +01002373 /* rfc3011 says this doesn't need to be in the requested options list. */
2374 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002375 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002376
2377 if (lease_time != 0xffffffff)
2378 {
2379 unsigned int t1val = lease_time/2;
2380 unsigned int t2val = (lease_time*7)/8;
2381 unsigned int hval;
2382
2383 /* If set by user, sanity check, so not longer than lease. */
2384 if ((opt = option_find2(OPTION_T1)))
2385 {
2386 hval = ntohl(*((unsigned int *)opt->val));
2387 if (hval < lease_time && hval > 2)
2388 t1val = hval;
2389 }
2390
2391 if ((opt = option_find2(OPTION_T2)))
2392 {
2393 hval = ntohl(*((unsigned int *)opt->val));
2394 if (hval < lease_time && hval > 2)
2395 t2val = hval;
2396 }
2397
Simon Kelley7c0f2542015-05-14 21:16:18 +01002398 /* ensure T1 is still < T2 */
2399 if (t2val <= t1val)
2400 t1val = t2val - 1;
2401
Simon Kelleyca85a282015-05-13 22:33:04 +01002402 while (fuzz > (t1val/8))
2403 fuzz = fuzz/2;
2404
2405 t1val -= fuzz;
2406 t2val -= fuzz;
2407
Simon Kelleyca85a282015-05-13 22:33:04 +01002408 option_put(mess, end, OPTION_T1, 4, t1val);
2409 option_put(mess, end, OPTION_T2, 4, t2val);
2410 }
2411
Simon Kelley73a08a22009-02-05 20:28:08 +00002412 /* replies to DHCPINFORM may not have a valid context */
2413 if (context)
2414 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002415 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002416 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2417
2418 /* May not have a "guessed" broadcast address if we got no packets via a relay
2419 from this net yet (ie just unicast renewals after a restart */
2420 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002421 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002422 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2423
2424 /* Same comments as broadcast apply, and also may not be able to get a sensible
2425 default when using subnet select. User must configure by steam in that case. */
2426 if (context->router.s_addr &&
2427 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002428 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002429 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2430
Simon Kelleya21e27b2013-02-17 16:41:35 +00002431 if (daemon->port == NAMESERVER_PORT &&
2432 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002433 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002434 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2435 }
Simon Kelley3be34542004-09-11 19:12:13 +01002436
Simon Kelley9009d742008-11-14 20:04:27 +00002437 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002438 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002439 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002440
Simon Kelley824af852008-02-12 20:43:05 +00002441 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002442 if (hostname)
2443 {
Simon Kelley824af852008-02-12 20:43:05 +00002444 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002445 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002446 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002447
2448 if (fqdn_flags != 0)
2449 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002450 len = strlen(hostname) + 3;
2451
Simon Kelley3d8df262005-08-29 12:19:27 +01002452 if (fqdn_flags & 0x04)
2453 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002454 else if (null_term)
2455 len++;
2456
Simon Kelley9009d742008-11-14 20:04:27 +00002457 if (domain)
2458 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002459 else if (fqdn_flags & 0x04)
2460 len--;
2461
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002462 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002463 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002464 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002465 *(p++) = 255;
2466 *(p++) = 255;
2467
2468 if (fqdn_flags & 0x04)
2469 {
Simon Kelley0549c732017-09-25 18:17:11 +01002470 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002471 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002472 {
Simon Kelley0549c732017-09-25 18:17:11 +01002473 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002474 *p++ = 0;
2475 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002476 }
2477 else
2478 {
2479 memcpy(p, hostname, strlen(hostname));
2480 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002481 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002482 {
2483 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002484 memcpy(p, domain, strlen(domain));
2485 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002486 }
2487 if (null_term)
2488 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002489 }
2490 }
2491 }
2492 }
2493
Simon Kelley6b010842007-02-12 20:32:07 +00002494 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002495 {
Simon Kelley824af852008-02-12 20:43:05 +00002496 int optno = opt->opt;
2497
Simon Kelley7de060b2011-08-26 17:24:52 +01002498 /* netids match and not encapsulated? */
2499 if (!(opt->flags & DHOPT_TAGOK))
2500 continue;
2501
Simon Kelley6b010842007-02-12 20:32:07 +00002502 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002503 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002504 continue;
2505
Simon Kelleyca85a282015-05-13 22:33:04 +01002506 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002507 if (optno == OPTION_CLIENT_FQDN ||
2508 optno == OPTION_MAXMESSAGE ||
2509 optno == OPTION_OVERLOAD ||
2510 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002511 optno == OPTION_END ||
2512 optno == OPTION_T1 ||
2513 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002514 continue;
2515
2516 if (optno == OPTION_SNAME && done_server)
2517 continue;
2518
2519 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002520 continue;
2521
Simon Kelley33820b72004-04-03 21:10:00 +01002522 /* For the options we have default values on
2523 dhc-option=<optionno> means "don't include this option"
2524 not "include a zero-length option" */
2525 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002526 (optno == OPTION_NETMASK ||
2527 optno == OPTION_BROADCAST ||
2528 optno == OPTION_ROUTER ||
2529 optno == OPTION_DNSSERVER ||
2530 optno == OPTION_DOMAINNAME ||
2531 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002532 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002533
2534 /* vendor-class comes from elsewhere for PXE */
2535 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2536 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002537
Simon Kelley7622fc02009-06-04 20:32:05 +01002538 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002539 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002540 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002541
Simon Kelley824af852008-02-12 20:43:05 +00002542 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002543 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002544 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002545 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2546
Simon Kelley6b010842007-02-12 20:32:07 +00002547 /* If we send a vendor-id, revisit which vendor-ops we consider
2548 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002549 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002550 {
2551 match_vendor_opts(p - 2, config_opts);
2552 done_vendor_class = 1;
2553 }
Simon Kelley6b010842007-02-12 20:32:07 +00002554 }
2555 }
2556
Simon Kelley73a08a22009-02-05 20:28:08 +00002557 /* Now send options to be encapsulated in arbitrary options,
2558 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002559 Also handle vendor-identifying vendor-encapsulated options,
2560 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002561 The may be more that one "outer" to do, so group
2562 all the options which match each outer in turn. */
2563 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002564 opt->flags &= ~DHOPT_ENCAP_DONE;
2565
2566 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002567 {
2568 int flags;
2569
2570 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2571 {
2572 int found = 0;
2573 struct dhcp_opt *o;
2574
2575 if (opt->flags & DHOPT_ENCAP_DONE)
2576 continue;
2577
2578 for (len = 0, o = config_opts; o; o = o->next)
2579 {
2580 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2581
2582 o->flags &= ~DHOPT_ENCAP_MATCH;
2583
2584 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2585 continue;
2586
2587 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002588 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002589 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2590 {
2591 o->flags |= DHOPT_ENCAP_MATCH;
2592 found = 1;
2593 len += do_opt(o, NULL, NULL, 0) + 2;
2594 }
2595 }
2596
2597 if (found)
2598 {
2599 if (flags & DHOPT_ENCAPSULATE)
2600 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2601 else if (len > 250)
2602 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2603 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2604 {
2605 int swap_ent = htonl(opt->u.encap);
2606 memcpy(p, &swap_ent, 4);
2607 p += 4;
2608 *(p++) = len;
2609 for (o = config_opts; o; o = o->next)
2610 if (o->flags & DHOPT_ENCAP_MATCH)
2611 {
2612 len = do_opt(o, p + 2, NULL, 0);
2613 *(p++) = o->opt;
2614 *(p++) = len;
2615 p += len;
2616 }
2617 }
2618 }
2619 }
2620 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002621
Simon Kelley7de060b2011-08-26 17:24:52 +01002622 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002623
Simon Kelley316e2732010-01-22 20:16:09 +00002624 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002625 {
2626 pxe_misc(mess, end, uuid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002627 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002628 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002629 }
2630
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002631 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2632 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2633 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2634 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2635 /* If we send vendor encapsulated options, and haven't already sent option 60,
2636 echo back the value we got from the client. */
2637 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2638
Simon Kelley7622fc02009-06-04 20:32:05 +01002639 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002640 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002641 {
2642 mess->file[0] = f0;
2643 mess->sname[0] = s0;
2644 }
2645}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002646
Floris Bos503c6092017-04-09 23:07:13 +01002647static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2648{
2649 struct delay_config *delay_conf;
2650
2651 /* Decide which delay_config option we're using */
2652 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2653 if (match_netid(delay_conf->netid, netid, 0))
2654 break;
2655
2656 if (!delay_conf)
2657 /* No match, look for one without a netid */
2658 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2659 if (match_netid(delay_conf->netid, netid, 1))
2660 break;
2661
2662 if (delay_conf)
2663 {
2664 if (!option_bool(OPT_QUIET_DHCP))
2665 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2666 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2667 }
2668}
2669
Simon Kelley7622fc02009-06-04 20:32:05 +01002670#endif
2671
2672
2673
2674
2675
2676
2677