blob: c08a8abb0aeb644488c1fae04b935b57ebb94d36 [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 Kelley7622fc02009-06-04 20:32:05 +010078 int clid_len = 0, ignore = 0, do_classes = 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);
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001076 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001077
Simon Kelley824af852008-02-12 20:43:05 +00001078 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001079 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001080 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001081 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001082 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001083 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001084 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001085 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001086
Simon Kelley7de060b2011-08-26 17:24:52 +01001087 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001088
1089 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001090 if (ignore || have_config(config, CONFIG_DISABLE))
1091 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001092 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001093 {
1094 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001095 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001096
Simon Kelley4011c4e2006-10-28 16:26:19 +01001097 /* send vendor and user class info for new or recreated lease */
1098 do_classes = 1;
1099
Simon Kelleybb01cb92004-12-13 20:56:23 +00001100 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001101 {
Simon Kelley3be34542004-09-11 19:12:13 +01001102 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001103 selecting = 1;
1104
Simon Kelley1a6bca82008-07-11 11:11:42 +01001105 if (override.s_addr != 0)
1106 {
1107 if (option_addr(opt).s_addr != override.s_addr)
1108 return 0;
1109 }
Simon Kelley9009d742008-11-14 20:04:27 +00001110 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001111 {
1112 for (; context; context = context->current)
1113 if (context->local.s_addr == option_addr(opt).s_addr)
1114 break;
1115
1116 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001117 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001118 /* Handle very strange configs where clients have more than one route to the server.
1119 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1120 Have to set override to make sure we echo back the correct server-id */
1121 struct irec *intr;
1122
Simon Kelley115ac3e2013-05-20 11:28:32 +01001123 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001124
1125 for (intr = daemon->interfaces; intr; intr = intr->next)
1126 if (intr->addr.sa.sa_family == AF_INET &&
1127 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1128 intr->tftp_ok)
1129 break;
1130
1131 if (intr)
1132 override = intr->addr.in.sin_addr;
1133 else
1134 {
1135 /* In auth mode, a REQUEST sent to the wrong server
1136 should be faulted, so that the client establishes
1137 communication with us, otherwise, silently ignore. */
1138 if (!option_bool(OPT_AUTHORITATIVE))
1139 return 0;
1140 message = _("wrong server-ID");
1141 }
Simon Kelley9009d742008-11-14 20:04:27 +00001142 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001143 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001144
Simon Kelley3be34542004-09-11 19:12:13 +01001145 /* If a lease exists for this host and another address, squash it. */
1146 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1147 {
1148 lease_prune(lease, now);
1149 lease = NULL;
1150 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001151 }
Simon Kelley3be34542004-09-11 19:12:13 +01001152 else
1153 {
1154 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001155 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001156 return 0;
1157
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001158 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001159 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001160 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001161 }
1162 else
1163 {
1164 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001165 /* Check existing lease for this address.
1166 We allow it to be missing if dhcp-authoritative mode
1167 as long as we can allocate the lease now - checked below.
1168 This makes for a smooth recovery from a lost lease DB */
1169 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001170 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001171 {
Simon Kelley28866e92011-02-14 20:19:14 +00001172 /* A client rebinding will broadcast the request, so we may see it even
1173 if the lease is held by another server. Just ignore it in that case.
1174 If the request is unicast to us, then somethings wrong, NAK */
1175 if (!unicast_dest)
1176 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001177 message = _("lease not found");
1178 /* ensure we broadcast NAK */
1179 unicast_dest = 0;
1180 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001181
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001182 /* desynchronise renewals */
1183 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001184 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001185 }
1186
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001187 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001188
Simon Kelleydfa666f2004-08-02 18:27:27 +01001189 if (!message)
1190 {
1191 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001192 struct dhcp_context *tmp = NULL;
1193
1194 if (have_config(config, CONFIG_ADDR))
1195 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001196 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001197 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001198
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001199 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001200 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001201 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001202 message = _("wrong network");
1203 /* ensure we broadcast NAK */
1204 unicast_dest = 0;
1205 }
1206
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001207 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001208 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001209 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001210 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001211
Simon Kelleydfa666f2004-08-02 18:27:27 +01001212 /* Check if a new static address has been configured. Be very sure that
1213 when the client does DISCOVER, it will get the static address, otherwise
1214 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001215 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001216 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001217 (!have_config(config, CONFIG_DECLINED) ||
1218 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001219 config->addr.s_addr != mess->yiaddr.s_addr &&
1220 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001221 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001222
1223 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001224 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001225 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001226
Simon Kelley9009d742008-11-14 20:04:27 +00001227 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1228 {
1229 /* If a host is configured with more than one MAC address, it's OK to 'nix
1230 a lease from one of it's MACs to give the address to another. */
1231 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1232 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001233 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001234 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1235 inet_ntoa(ltmp->addr));
1236 lease = ltmp;
1237 }
Simon Kelley16972692006-10-16 20:04:18 +01001238 else
Simon Kelley9009d742008-11-14 20:04:27 +00001239 message = _("address in use");
1240 }
1241
1242 if (!message)
1243 {
1244 if (emac_len == 0)
1245 message = _("no unique-id");
1246
1247 else if (!lease)
1248 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001249 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001250 do_classes = 1;
1251 else
1252 message = _("no leases left");
1253 }
Simon Kelley16972692006-10-16 20:04:18 +01001254 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001255 }
Simon Kelley16972692006-10-16 20:04:18 +01001256
Simon Kelley44a2a312004-03-10 20:04:35 +00001257 if (message)
1258 {
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001259 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001260
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001261 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001262 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001263 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001264 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001265 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001266 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1267 a distant subnet which unicast a REQ to us won't work. */
1268 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1269 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1270 {
1271 mess->flags |= htons(0x8000); /* broadcast */
1272 mess->ciaddr.s_addr = 0;
1273 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001274 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001275 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001276 {
Simon Kelley316e2732010-01-22 20:16:09 +00001277 if (context->netid.net)
1278 {
1279 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001280 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001281 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001282
Simon Kelley4cb1b322012-02-06 14:30:41 +00001283 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001284
Simon Kelleydcffad22012-04-24 15:25:18 +01001285 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001286 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001287 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001288 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001289
Simon Kelleydcffad22012-04-24 15:25:18 +01001290#ifdef HAVE_SCRIPT
1291 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001292 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001293 struct dhcp_netid *n;
1294
1295 if (mess->giaddr.s_addr)
1296 lease->giaddr = mess->giaddr;
1297
1298 free(lease->extradata);
1299 lease->extradata = NULL;
1300 lease->extradata_size = lease->extradata_len = 0;
1301
1302 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1303 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1304 add_extradata_opt(lease, oui);
1305 add_extradata_opt(lease, serial);
1306 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001307
1308 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1309 {
1310 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1311 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1312 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1313 }
1314 else
1315 {
1316 add_extradata_opt(lease, NULL);
1317 add_extradata_opt(lease, NULL);
1318 add_extradata_opt(lease, NULL);
1319 }
1320
ZHAO Yuf89cae32016-12-22 22:32:31 +00001321 /* DNSMASQ_REQUESTED_OPTIONS */
1322 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1323 {
1324 int len = option_len(opt);
1325 unsigned char *rop = option_ptr(opt, 0);
1326 char *q = daemon->namebuff;
1327 int i;
1328 for (i = 0; i < len; i++)
1329 {
1330 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1331 }
1332 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1333 }
1334 else
1335 {
1336 add_extradata_opt(lease, NULL);
1337 }
1338
Simon Kelleydcffad22012-04-24 15:25:18 +01001339 /* space-concat tag set */
1340 if (!tagif_netid)
1341 add_extradata_opt(lease, NULL);
1342 else
1343 for (n = tagif_netid; n; n = n->next)
1344 {
1345 struct dhcp_netid *n1;
1346 /* kill dupes */
1347 for (n1 = n->next; n1; n1 = n1->next)
1348 if (strcmp(n->net, n1->net) == 0)
1349 break;
1350 if (!n1)
1351 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1352 }
1353
1354 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1355 {
1356 int len = option_len(opt);
1357 unsigned char *ucp = option_ptr(opt, 0);
1358 /* If the user-class option started as counted strings, the first byte will be zero. */
1359 if (len != 0 && ucp[0] == 0)
1360 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001361 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001362 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001363 }
Simon Kelley316e2732010-01-22 20:16:09 +00001364#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001365 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001366
1367 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1368 {
1369 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001370 hostname = client_hostname;
1371 hostname_auth = 1;
1372 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001373
Simon Kelley824af852008-02-12 20:43:05 +00001374 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001375 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001376
Simon Kelley832af0b2007-01-21 20:01:28 +00001377 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1378 if (!hostname_auth)
1379 {
1380 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001381 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001382 break;
1383 if (id_list)
1384 hostname = NULL;
1385 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001386
1387 /* Last ditch, if configured, generate hostname from mac address */
1388 if (!hostname && emac_len != 0)
1389 {
1390 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1391 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1392 break;
1393 if (id_list)
1394 {
1395 int i;
1396
1397 hostname = daemon->dhcp_buff;
1398 /* buffer is 256 bytes, 3 bytes per octet */
1399 for (i = 0; (i < emac_len) && (i < 80); i++)
1400 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1401 hostname = daemon->dhcp_buff;
1402 }
1403 }
1404
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001405 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001406 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001407
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001408 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001409 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001410
1411 if (override.s_addr != 0)
1412 lease->override = override;
1413 else
1414 override = lease->override;
1415
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001416 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001417
Simon Kelleya9df0e32017-04-28 22:43:00 +01001418 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001419 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001420 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001421 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley9009d742008-11-14 20:04:27 +00001422 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001423 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001424 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001425
Simon Kelley7de060b2011-08-26 17:24:52 +01001426 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001427
1428 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001429 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001430 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001431
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001432 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001433
Simon Kelley73a08a22009-02-05 20:28:08 +00001434 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001435 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001436
1437 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001438 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001439
Simon Kelley5aabfc72007-08-29 11:24:47 +01001440 /* Find a least based on IP address if we didn't
1441 get one from MAC address/client-d */
1442 if (!lease &&
1443 (lease = lease_find_by_addr(mess->ciaddr)) &&
1444 lease->hostname)
1445 hostname = lease->hostname;
1446
Simon Kelley0f371f92013-07-27 15:15:38 +01001447 if (!hostname)
1448 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001449
Simon Kelley73a08a22009-02-05 20:28:08 +00001450 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001451 {
1452 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001453 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001454 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001455
Simon Kelley4cb1b322012-02-06 14:30:41 +00001456 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001457
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001458 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001459
Simon Kelley3927da42008-07-20 15:10:39 +01001460 if (lease)
1461 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001462 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001463 if (override.s_addr != 0)
1464 lease->override = override;
1465 else
1466 override = lease->override;
1467 }
1468
Simon Kelleya9df0e32017-04-28 22:43:00 +01001469 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001470 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001471 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001472
1473 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1474 we supply a utility which makes DHCPINFORM requests to get this information.
1475 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1476 which won't be true for ordinary clients, but will be true for the
1477 dhcp_lease_time utility. */
1478 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1479 {
1480 if (lease->expires == 0)
1481 time = 0xffffffff;
1482 else
1483 time = (unsigned int)difftime(lease->expires, now);
1484 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1485 }
1486
Simon Kelley9009d742008-11-14 20:04:27 +00001487 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001488 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001489
Simon Kelley5aabfc72007-08-29 11:24:47 +01001490 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001491 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001492 }
1493
1494 return 0;
1495}
1496
Simon Kelley6b010842007-02-12 20:32:07 +00001497/* find a good value to use as MAC address for logging and address-allocation hashing.
1498 This is normally just the chaddr field from the DHCP packet,
1499 but eg Firewire will have hlen == 0 and use the client-id instead.
1500 This could be anything, but will normally be EUI64 for Firewire.
1501 We assume that if the first byte of the client-id equals the htype byte
1502 then the client-id is using the usual encoding and use the rest of the
1503 client-id: if not we can use the whole client-id. This should give
1504 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001505unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001506 int clid_len, unsigned char *clid, int *len_out)
1507{
1508 if (hwlen == 0 && clid && clid_len > 3)
1509 {
1510 if (clid[0] == hwtype)
1511 {
1512 *len_out = clid_len - 1 ;
1513 return clid + 1;
1514 }
1515
1516#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1517 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1518 {
1519 *len_out = clid_len - 1 ;
1520 return clid + 1;
1521 }
1522#endif
1523
1524 *len_out = clid_len;
1525 return clid;
1526 }
1527
1528 *len_out = hwlen;
1529 return hwaddr;
1530}
1531
Simon Kelley824af852008-02-12 20:43:05 +00001532static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001533{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001534 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001535
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001536 if (opt)
1537 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001538 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001539 if (req_time < 120 )
1540 req_time = 120; /* sanity */
1541 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1542 time = req_time;
1543 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001544
1545 return time;
1546}
1547
Simon Kelley73a08a22009-02-05 20:28:08 +00001548static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001549{
Simon Kelley73a08a22009-02-05 20:28:08 +00001550 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001551 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001552 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001553 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001554 else
1555 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001556}
1557
Simon Kelleyf2621c72007-04-29 19:47:21 +01001558static int sanitise(unsigned char *opt, char *buf)
1559{
1560 char *p;
1561 int i;
1562
1563 *buf = 0;
1564
1565 if (!opt)
1566 return 0;
1567
Simon Kelley1a6bca82008-07-11 11:11:42 +01001568 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001569
1570 for (i = option_len(opt); i > 0; i--)
1571 {
1572 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001573 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001574 *buf++ = c;
1575 }
1576 *buf = 0; /* add terminator */
1577
1578 return 1;
1579}
1580
Simon Kelley316e2732010-01-22 20:16:09 +00001581#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001582static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1583{
1584 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001585 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001586 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001587 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001588}
1589#endif
1590
Simon Kelley5aabfc72007-08-29 11:24:47 +01001591static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001592 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001593{
Simon Kelley16972692006-10-16 20:04:18 +01001594 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001595
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001596 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001597 return;
1598
Simon Kelley16972692006-10-16 20:04:18 +01001599 /* addr may be misaligned */
1600 if (addr)
1601 memcpy(&a, addr, sizeof(a));
1602
Simon Kelley7622fc02009-06-04 20:32:05 +01001603 print_mac(daemon->namebuff, ext_mac, mac_len);
1604
Simon Kelley28866e92011-02-14 20:19:14 +00001605 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001606 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001607 ntohl(xid),
1608 type,
1609 interface,
1610 addr ? inet_ntoa(a) : "",
1611 addr ? " " : "",
1612 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001613 string ? string : "",
1614 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001615 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001616 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001617 type,
1618 interface,
1619 addr ? inet_ntoa(a) : "",
1620 addr ? " " : "",
1621 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001622 string ? string : "",
1623 err ? err : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001624}
1625
Simon Kelley7622fc02009-06-04 20:32:05 +01001626static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001627{
1628 while (*start != OPTION_END)
1629 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001630 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 +01001631
Simon Kelley4cb1b322012-02-06 14:30:41 +00001632 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1633 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001634 start += start[1] + 2;
1635 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001636}
1637
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001638static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001639{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001640 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001641 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001642 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001643 return NULL;
1644 else if (*p == OPTION_END)
1645 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001646 else if (*p == OPTION_PAD)
1647 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001648 else
1649 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001650 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001651 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001652 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001653 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001654 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001655 return NULL; /* malformed packet */
1656 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001657 return p;
1658 p += opt_len + 2;
1659 }
1660 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001661}
1662
Simon Kelleycdeda282006-03-16 20:16:06 +00001663static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001664{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001665 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001666
Simon Kelley3be34542004-09-11 19:12:13 +01001667 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001668 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1669 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001670
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001671 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001672 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001673 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001674
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001675 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001676 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001677 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1678 return ret;
1679
1680 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001681 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001682 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1683 return ret;
1684
1685 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001686}
1687
Simon Kelley4cb1b322012-02-06 14:30:41 +00001688static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001689{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001690 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001691 /* struct in_addr is network byte order */
1692 struct in_addr ret;
1693
Simon Kelley4cb1b322012-02-06 14:30:41 +00001694 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001695
1696 return ret;
1697}
1698
Simon Kelley7622fc02009-06-04 20:32:05 +01001699static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001700{
1701 /* this worries about unaligned data and byte order */
1702 unsigned int ret = 0;
1703 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001704 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001705
1706 for (i = 0; i < size; i++)
1707 ret = (ret << 8) | *p++;
1708
1709 return ret;
1710}
1711
1712static unsigned char *dhcp_skip_opts(unsigned char *start)
1713{
1714 while (*start != 0)
1715 start += start[1] + 2;
1716 return start;
1717}
1718
1719/* only for use when building packet: doesn't check for bad data. */
1720static unsigned char *find_overload(struct dhcp_packet *mess)
1721{
1722 unsigned char *p = &mess->options[0] + sizeof(u32);
1723
1724 while (*p != 0)
1725 {
1726 if (*p == OPTION_OVERLOAD)
1727 return p;
1728 p += p[1] + 2;
1729 }
1730 return NULL;
1731}
1732
Simon Kelley7de060b2011-08-26 17:24:52 +01001733static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1734{
1735 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1736 unsigned char *overload;
1737 size_t ret;
1738
1739 /* move agent_id back down to the end of the packet */
1740 if (agent_id)
1741 {
1742 memmove(p, agent_id, real_end - agent_id);
1743 p += real_end - agent_id;
1744 memset(p, 0, real_end - p); /* in case of overlap */
1745 }
1746
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001747 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001748 overload = find_overload(mess);
1749
1750 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001751 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001752 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001753 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001754 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001755 }
Simon Kelley28866e92011-02-14 20:19:14 +00001756 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001757 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1758
1759 if (overload && (option_uint(overload, 0, 1) & 2))
1760 {
1761 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001762 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001763 log_options(mess->sname, mess->xid);
1764 }
Simon Kelley28866e92011-02-14 20:19:14 +00001765 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001766 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1767
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001768
1769 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001770
Simon Kelley28866e92011-02-14 20:19:14 +00001771 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001772 {
1773 if (mess->siaddr.s_addr != 0)
1774 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1775
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001776 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1777 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1778
Simon Kelley7622fc02009-06-04 20:32:05 +01001779 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1780 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001781
1782 ret = (size_t)(p - (unsigned char *)mess);
1783
1784 if (ret < MIN_PACKETSZ)
1785 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001786
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001787 return ret;
1788}
1789
1790static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1791{
1792 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1793
1794 if (p + len + 3 >= end)
1795 /* not enough space in options area, try and use overload, if poss */
1796 {
1797 unsigned char *overload;
1798
1799 if (!(overload = find_overload(mess)) &&
1800 (mess->file[0] == 0 || mess->sname[0] == 0))
1801 {
1802 /* attempt to overload fname and sname areas, we've reserved space for the
1803 overflow option previuously. */
1804 overload = p;
1805 *(p++) = OPTION_OVERLOAD;
1806 *(p++) = 1;
1807 }
1808
1809 p = NULL;
1810
1811 /* using filename field ? */
1812 if (overload)
1813 {
1814 if (mess->file[0] == 0)
1815 overload[2] |= 1;
1816
1817 if (overload[2] & 1)
1818 {
1819 p = dhcp_skip_opts(mess->file);
1820 if (p + len + 3 >= mess->file + sizeof(mess->file))
1821 p = NULL;
1822 }
1823
1824 if (!p)
1825 {
1826 /* try to bring sname into play (it may be already) */
1827 if (mess->sname[0] == 0)
1828 overload[2] |= 2;
1829
1830 if (overload[2] & 2)
1831 {
1832 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001833 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001834 p = NULL;
1835 }
1836 }
1837 }
1838
1839 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001840 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 +00001841 }
1842
1843 if (p)
1844 {
1845 *(p++) = opt;
1846 *(p++) = len;
1847 }
1848
1849 return p;
1850}
1851
1852static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1853{
1854 int i;
1855 unsigned char *p = free_space(mess, end, opt, len);
1856
1857 if (p)
1858 for (i = 0; i < len; i++)
1859 *(p++) = val >> (8 * (len - (i + 1)));
1860}
1861
1862static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1863 char *string, int null_term)
1864{
1865 unsigned char *p;
1866 size_t len = strlen(string);
1867
1868 if (null_term && len != 255)
1869 len++;
1870
1871 if ((p = free_space(mess, end, opt, len)))
1872 memcpy(p, string, len);
1873}
1874
1875/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001876static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001877{
1878 int len = opt->len;
1879
1880 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1881 len++;
1882
1883 if (p && len != 0)
1884 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001885 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001886 {
1887 int j;
1888 struct in_addr *a = (struct in_addr *)opt->val;
1889 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1890 {
1891 /* zero means "self" (but not in vendorclass options.) */
1892 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001893 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001894 else
1895 memcpy(p, a, INADDRSZ);
1896 p += INADDRSZ;
1897 }
1898 }
1899 else
Simon Kelley625ac282013-07-02 21:19:32 +01001900 /* empty string may be extended to "\0" by null_term */
1901 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001902 }
1903 return len;
1904}
Simon Kelley7622fc02009-06-04 20:32:05 +01001905
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001906static int in_list(unsigned char *list, int opt)
1907{
1908 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001909
1910 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001911 if (!list)
1912 return 1;
1913
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001914 for (i = 0; list[i] != OPTION_END; i++)
1915 if (opt == list[i])
1916 return 1;
1917
1918 return 0;
1919}
1920
Simon Kelley7de060b2011-08-26 17:24:52 +01001921static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001922{
Simon Kelley7de060b2011-08-26 17:24:52 +01001923 struct dhcp_opt *opts;
1924
1925 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1926 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1927 return opts;
1928
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001929 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001930}
1931
Simon Kelley6b010842007-02-12 20:32:07 +00001932/* mark vendor-encapsulated options which match the client-supplied or
1933 config-supplied vendor class */
1934static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1935{
1936 for (; dopt; dopt = dopt->next)
1937 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001938 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001939 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001940 {
1941 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001942 if (dopt->u.vendor_class)
1943 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001944 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001945 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001946 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001947 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001948 break;
1949 }
1950 }
1951 }
1952}
1953
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001954static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1955 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001956{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001957 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001958 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001959 unsigned char *p;
1960
1961 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001962 for (enc_len = 0, start = opt; opt; opt = opt->next)
1963 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001964 {
1965 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001966 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001967 if (enc_len + new <= 255)
1968 enc_len += new;
1969 else
1970 {
1971 p = free_space(mess, end, encap, enc_len);
1972 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001973 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001974 {
1975 len = do_opt(start, p + 2, NULL, null_term);
1976 *(p++) = start->opt;
1977 *(p++) = len;
1978 p += len;
1979 }
1980 enc_len = new;
1981 start = opt;
1982 }
1983 }
1984
1985 if (enc_len != 0 &&
1986 (p = free_space(mess, end, encap, enc_len + 1)))
1987 {
1988 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001989 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001990 {
1991 len = do_opt(start, p + 2, NULL, null_term);
1992 *(p++) = start->opt;
1993 *(p++) = len;
1994 p += len;
1995 }
1996 *p = OPTION_END;
1997 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001998
1999 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002000}
2001
Simon Kelley7622fc02009-06-04 20:32:05 +01002002static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002003{
Simon Kelley7622fc02009-06-04 20:32:05 +01002004 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002005
Simon Kelley7622fc02009-06-04 20:32:05 +01002006 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2007 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2008 memcpy(p, uuid, 17);
2009}
2010
2011static int prune_vendor_opts(struct dhcp_netid *netid)
2012{
2013 int force = 0;
2014 struct dhcp_opt *opt;
2015
2016 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2017 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2018 if (opt->flags & DHOPT_VENDOR_MATCH)
2019 {
2020 if (!match_netid(opt->netid, netid, 1))
2021 opt->flags &= ~DHOPT_VENDOR_MATCH;
2022 else if (opt->flags & DHOPT_FORCE)
2023 force = 1;
2024 }
2025 return force;
2026}
2027
Simon Kelley8628cd62016-05-10 17:31:48 +01002028
2029/* Many UEFI PXE implementations have badly broken menu code.
2030 If there's exactly one relevant menu item, we abandon the menu system,
2031 and jamb the data direct into the DHCP file, siaddr and sname fields.
2032 Note that in this case, we have to assume that layer zero would be requested
2033 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002034static 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 +01002035{
2036 struct pxe_service *service, *found;
2037
2038 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002039 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002040 return 0;
2041
2042 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002043 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002044 {
2045 if (found)
2046 return 0; /* More than one relevant menu item */
2047
2048 found = service;
2049 }
2050
2051 if (!found)
2052 return 0; /* No relevant menu items. */
2053
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002054 if (!pxe)
2055 return 1;
2056
Simon Kelley8628cd62016-05-10 17:31:48 +01002057 if (found->sname)
2058 {
2059 mess->siaddr = a_record_from_hosts(found->sname, now);
2060 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2061 }
2062 else
2063 {
2064 if (found->server.s_addr != 0)
2065 mess->siaddr = found->server;
2066 else
2067 mess->siaddr = local;
2068
2069 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2070 }
2071
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002072 snprintf((char *)mess->file, sizeof(mess->file),
2073 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2074
Simon Kelley8628cd62016-05-10 17:31:48 +01002075 return 1;
2076}
2077
Simon Kelley751d6f42012-02-10 15:24:51 +00002078static 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 +01002079{
2080#define NUM_OPTS 4
2081
2082 unsigned char *p, *q;
2083 struct pxe_service *service;
2084 static struct dhcp_opt *o, *ret;
2085 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002086 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002087
2088 /* We pass back references to these, hence they are declared static */
2089 static unsigned char discovery_control;
2090 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2091 static struct dhcp_opt *fake_opts = NULL;
2092
Simon Kelley316e2732010-01-22 20:16:09 +00002093 /* Disable multicast, since we don't support it, and broadcast
2094 unless we need it */
2095 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002096
2097 ret = daemon->dhcp_opts;
2098
2099 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2100 return ret;
2101
2102 for (i = 0; i < NUM_OPTS; i++)
2103 {
2104 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2105 fake_opts[i].netid = NULL;
2106 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2107 }
2108
2109 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2110 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002111 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002112
2113 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2114 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2115 {
2116 size_t len = strlen(service->menu);
2117 /* opt 43 max size is 255. encapsulated option has type and length
2118 bytes, so its max size is 253. */
2119 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2120 {
2121 *(p++) = service->type >> 8;
2122 *(p++) = service->type;
2123 *(p++) = len;
2124 memcpy(p, service->menu, len);
2125 p += len;
2126 i++;
2127 }
2128 else
2129 {
2130 toobig:
2131 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2132 return daemon->dhcp_opts;
2133 }
2134
Simon Kelley751d6f42012-02-10 15:24:51 +00002135 boot_server = service->basename ? local :
2136 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2137
Simon Kelley316e2732010-01-22 20:16:09 +00002138 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002139 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002140 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002141 goto toobig;
2142
2143 /* Boot service with known address - give it */
2144 *(q++) = service->type >> 8;
2145 *(q++) = service->type;
2146 *(q++) = 1;
2147 /* dest misaligned */
2148 memcpy(q, &boot_server.s_addr, INADDRSZ);
2149 q += INADDRSZ;
2150 }
2151 else if (service->type != 0)
2152 /* We don't know the server for a service type, so we'll
2153 allow the client to broadcast for it */
2154 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002155 }
2156
2157 /* if no prompt, wait forever if there's a choice */
2158 fake_prompt[0] = (i > 1) ? 255 : 0;
2159
2160 if (i == 0)
2161 discovery_control = 8; /* no menu - just use use mess->filename */
2162 else
2163 {
2164 ret = &fake_opts[j--];
2165 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2166 ret->val = (unsigned char *)daemon->dhcp_buff;
2167 ret->opt = SUBOPT_PXE_MENU;
2168
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002169 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002170 {
2171 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002172 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2173 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002174 ret->opt = SUBOPT_PXE_SERVERS;
2175 }
2176 }
2177
2178 for (o = daemon->dhcp_opts; o; o = o->next)
2179 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2180 break;
2181
2182 if (!o)
2183 {
2184 ret = &fake_opts[j--];
2185 ret->len = sizeof(fake_prompt);
2186 ret->val = fake_prompt;
2187 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2188 }
2189
Simon Kelley316e2732010-01-22 20:16:09 +00002190 ret = &fake_opts[j--];
2191 ret->len = 1;
2192 ret->opt = SUBOPT_PXE_DISCOVERY;
2193 ret->val= &discovery_control;
2194
Simon Kelley7622fc02009-06-04 20:32:05 +01002195 return ret;
2196}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002197
2198static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002199{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002200 memset(mess->sname, 0, sizeof(mess->sname));
2201 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002202 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002203 mess->siaddr.s_addr = 0;
2204}
Simon Kelleycdeda282006-03-16 20:16:06 +00002205
Simon Kelley7622fc02009-06-04 20:32:05 +01002206struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002207{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002208 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002209
2210 /* decide which dhcp-boot option we're using */
2211 for (boot = daemon->boot_config; boot; boot = boot->next)
2212 if (match_netid(boot->netid, netid, 0))
2213 break;
2214 if (!boot)
2215 /* No match, look for one without a netid */
2216 for (boot = daemon->boot_config; boot; boot = boot->next)
2217 if (match_netid(boot->netid, netid, 1))
2218 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002219
2220 return boot;
2221}
2222
2223static void do_options(struct dhcp_context *context,
2224 struct dhcp_packet *mess,
2225 unsigned char *end,
2226 unsigned char *req_options,
2227 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002228 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002229 struct dhcp_netid *netid,
2230 struct in_addr subnet_addr,
2231 unsigned char fqdn_flags,
2232 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002233 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002234 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002235 time_t now,
2236 unsigned int lease_time,
2237 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002238{
2239 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2240 struct dhcp_boot *boot;
2241 unsigned char *p;
2242 int i, len, force_encap = 0;
2243 unsigned char f0 = 0, s0 = 0;
2244 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002245 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002246 struct dhcp_netid *tagif;
2247 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002248
Simon Kelley4cb1b322012-02-06 14:30:41 +00002249 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002250 if (context)
2251 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002252 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002253
Simon Kelley7622fc02009-06-04 20:32:05 +01002254 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002255 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002256 {
2257 char *q = daemon->namebuff;
2258 for (i = 0; req_options[i] != OPTION_END; i++)
2259 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002260 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002261 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2262 "%d%s%s%s",
2263 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002264 strlen(s) != 0 ? ":" : "",
2265 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002266 req_options[i+1] == OPTION_END ? "" : ", ");
2267 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2268 {
2269 q = daemon->namebuff;
2270 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2271 }
2272 }
2273 }
2274
Simon Kelley7de060b2011-08-26 17:24:52 +01002275 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2276 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2277 break;
2278 if (id_list)
2279 mess->flags |= htons(0x8000); /* force broadcast */
2280
Simon Kelley73a08a22009-02-05 20:28:08 +00002281 if (context)
2282 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002283
2284 /* See if we can send the boot stuff as options.
2285 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002286 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002287 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002288 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002289 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002290 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002291 {
2292 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002293 {
Simon Kelley28866e92011-02-14 20:19:14 +00002294 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002295 req_options &&
2296 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002297 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2298 else
Simon Kelley824af852008-02-12 20:43:05 +00002299 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002300 }
2301
2302 if (boot->file)
2303 {
Simon Kelley28866e92011-02-14 20:19:14 +00002304 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002305 req_options &&
2306 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002307 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2308 else
Simon Kelley824af852008-02-12 20:43:05 +00002309 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002310 }
2311
Simon Kelley7de060b2011-08-26 17:24:52 +01002312 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002313 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002314 else if (boot->tftp_sname)
2315 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002316 }
Simon Kelley824af852008-02-12 20:43:05 +00002317 else
2318 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002319 they're not explicitly asked for as options. OPTION_END is used
2320 as an internal way to specify siaddr without using dhcp-boot, for use in
2321 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002322 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002323 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002324 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002325 {
2326 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2327 done_file = 1;
2328 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002329
Simon Kelley824af852008-02-12 20:43:05 +00002330 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002331 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002332 {
2333 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2334 done_server = 1;
2335 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002336
Simon Kelley7de060b2011-08-26 17:24:52 +01002337 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002338 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002339 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002340
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002341 /* We don't want to do option-overload for BOOTP, so make the file and sname
2342 fields look like they are in use, even when they aren't. This gets restored
2343 at the end of this function. */
2344
Simon Kelley28866e92011-02-14 20:19:14 +00002345 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002346 {
2347 f0 = mess->file[0];
2348 mess->file[0] = 1;
2349 s0 = mess->sname[0];
2350 mess->sname[0] = 1;
2351 }
2352
2353 /* At this point, if mess->sname or mess->file are zeroed, they are available
2354 for option overload, reserve space for the overload option. */
2355 if (mess->file[0] == 0 || mess->sname[0] == 0)
2356 end -= 3;
2357
Simon Kelley3be34542004-09-11 19:12:13 +01002358 /* rfc3011 says this doesn't need to be in the requested options list. */
2359 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002360 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002361
2362 if (lease_time != 0xffffffff)
2363 {
2364 unsigned int t1val = lease_time/2;
2365 unsigned int t2val = (lease_time*7)/8;
2366 unsigned int hval;
2367
2368 /* If set by user, sanity check, so not longer than lease. */
2369 if ((opt = option_find2(OPTION_T1)))
2370 {
2371 hval = ntohl(*((unsigned int *)opt->val));
2372 if (hval < lease_time && hval > 2)
2373 t1val = hval;
2374 }
2375
2376 if ((opt = option_find2(OPTION_T2)))
2377 {
2378 hval = ntohl(*((unsigned int *)opt->val));
2379 if (hval < lease_time && hval > 2)
2380 t2val = hval;
2381 }
2382
Simon Kelley7c0f2542015-05-14 21:16:18 +01002383 /* ensure T1 is still < T2 */
2384 if (t2val <= t1val)
2385 t1val = t2val - 1;
2386
Simon Kelleyca85a282015-05-13 22:33:04 +01002387 while (fuzz > (t1val/8))
2388 fuzz = fuzz/2;
2389
2390 t1val -= fuzz;
2391 t2val -= fuzz;
2392
Simon Kelleyca85a282015-05-13 22:33:04 +01002393 option_put(mess, end, OPTION_T1, 4, t1val);
2394 option_put(mess, end, OPTION_T2, 4, t2val);
2395 }
2396
Simon Kelley73a08a22009-02-05 20:28:08 +00002397 /* replies to DHCPINFORM may not have a valid context */
2398 if (context)
2399 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002400 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002401 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2402
2403 /* May not have a "guessed" broadcast address if we got no packets via a relay
2404 from this net yet (ie just unicast renewals after a restart */
2405 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002406 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002407 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2408
2409 /* Same comments as broadcast apply, and also may not be able to get a sensible
2410 default when using subnet select. User must configure by steam in that case. */
2411 if (context->router.s_addr &&
2412 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002413 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002414 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2415
Simon Kelleya21e27b2013-02-17 16:41:35 +00002416 if (daemon->port == NAMESERVER_PORT &&
2417 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002418 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002419 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2420 }
Simon Kelley3be34542004-09-11 19:12:13 +01002421
Simon Kelley9009d742008-11-14 20:04:27 +00002422 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002423 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002424 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002425
Simon Kelley824af852008-02-12 20:43:05 +00002426 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002427 if (hostname)
2428 {
Simon Kelley824af852008-02-12 20:43:05 +00002429 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002430 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002431 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002432
2433 if (fqdn_flags != 0)
2434 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002435 len = strlen(hostname) + 3;
2436
Simon Kelley3d8df262005-08-29 12:19:27 +01002437 if (fqdn_flags & 0x04)
2438 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002439 else if (null_term)
2440 len++;
2441
Simon Kelley9009d742008-11-14 20:04:27 +00002442 if (domain)
2443 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002444 else if (fqdn_flags & 0x04)
2445 len--;
2446
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002447 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002448 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002449 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002450 *(p++) = 255;
2451 *(p++) = 255;
2452
2453 if (fqdn_flags & 0x04)
2454 {
Simon Kelley0549c732017-09-25 18:17:11 +01002455 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002456 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002457 {
Simon Kelley0549c732017-09-25 18:17:11 +01002458 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002459 *p++ = 0;
2460 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002461 }
2462 else
2463 {
2464 memcpy(p, hostname, strlen(hostname));
2465 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002466 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002467 {
2468 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002469 memcpy(p, domain, strlen(domain));
2470 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002471 }
2472 if (null_term)
2473 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002474 }
2475 }
2476 }
2477 }
2478
Simon Kelley6b010842007-02-12 20:32:07 +00002479 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002480 {
Simon Kelley824af852008-02-12 20:43:05 +00002481 int optno = opt->opt;
2482
Simon Kelley7de060b2011-08-26 17:24:52 +01002483 /* netids match and not encapsulated? */
2484 if (!(opt->flags & DHOPT_TAGOK))
2485 continue;
2486
Simon Kelley6b010842007-02-12 20:32:07 +00002487 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002488 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002489 continue;
2490
Simon Kelleyca85a282015-05-13 22:33:04 +01002491 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002492 if (optno == OPTION_CLIENT_FQDN ||
2493 optno == OPTION_MAXMESSAGE ||
2494 optno == OPTION_OVERLOAD ||
2495 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002496 optno == OPTION_END ||
2497 optno == OPTION_T1 ||
2498 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002499 continue;
2500
2501 if (optno == OPTION_SNAME && done_server)
2502 continue;
2503
2504 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002505 continue;
2506
Simon Kelley33820b72004-04-03 21:10:00 +01002507 /* For the options we have default values on
2508 dhc-option=<optionno> means "don't include this option"
2509 not "include a zero-length option" */
2510 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002511 (optno == OPTION_NETMASK ||
2512 optno == OPTION_BROADCAST ||
2513 optno == OPTION_ROUTER ||
2514 optno == OPTION_DNSSERVER ||
2515 optno == OPTION_DOMAINNAME ||
2516 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002517 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002518
2519 /* vendor-class comes from elsewhere for PXE */
2520 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2521 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002522
Simon Kelley7622fc02009-06-04 20:32:05 +01002523 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002524 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002525 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002526
Simon Kelley824af852008-02-12 20:43:05 +00002527 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002528 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002529 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002530 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2531
Simon Kelley6b010842007-02-12 20:32:07 +00002532 /* If we send a vendor-id, revisit which vendor-ops we consider
2533 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002534 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002535 {
2536 match_vendor_opts(p - 2, config_opts);
2537 done_vendor_class = 1;
2538 }
Simon Kelley6b010842007-02-12 20:32:07 +00002539 }
2540 }
2541
Simon Kelley73a08a22009-02-05 20:28:08 +00002542 /* Now send options to be encapsulated in arbitrary options,
2543 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002544 Also handle vendor-identifying vendor-encapsulated options,
2545 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002546 The may be more that one "outer" to do, so group
2547 all the options which match each outer in turn. */
2548 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002549 opt->flags &= ~DHOPT_ENCAP_DONE;
2550
2551 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002552 {
2553 int flags;
2554
2555 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2556 {
2557 int found = 0;
2558 struct dhcp_opt *o;
2559
2560 if (opt->flags & DHOPT_ENCAP_DONE)
2561 continue;
2562
2563 for (len = 0, o = config_opts; o; o = o->next)
2564 {
2565 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2566
2567 o->flags &= ~DHOPT_ENCAP_MATCH;
2568
2569 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2570 continue;
2571
2572 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002573 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002574 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2575 {
2576 o->flags |= DHOPT_ENCAP_MATCH;
2577 found = 1;
2578 len += do_opt(o, NULL, NULL, 0) + 2;
2579 }
2580 }
2581
2582 if (found)
2583 {
2584 if (flags & DHOPT_ENCAPSULATE)
2585 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2586 else if (len > 250)
2587 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2588 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2589 {
2590 int swap_ent = htonl(opt->u.encap);
2591 memcpy(p, &swap_ent, 4);
2592 p += 4;
2593 *(p++) = len;
2594 for (o = config_opts; o; o = o->next)
2595 if (o->flags & DHOPT_ENCAP_MATCH)
2596 {
2597 len = do_opt(o, p + 2, NULL, 0);
2598 *(p++) = o->opt;
2599 *(p++) = len;
2600 p += len;
2601 }
2602 }
2603 }
2604 }
2605 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002606
Simon Kelley7de060b2011-08-26 17:24:52 +01002607 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002608
Simon Kelley316e2732010-01-22 20:16:09 +00002609 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002610 {
2611 pxe_misc(mess, end, uuid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002612 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002613 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002614 }
2615
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002616 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2617 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2618 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2619 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2620 /* If we send vendor encapsulated options, and haven't already sent option 60,
2621 echo back the value we got from the client. */
2622 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2623
Simon Kelley7622fc02009-06-04 20:32:05 +01002624 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002625 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002626 {
2627 mess->file[0] = f0;
2628 mess->sname[0] = s0;
2629 }
2630}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002631
Floris Bos503c6092017-04-09 23:07:13 +01002632static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2633{
2634 struct delay_config *delay_conf;
2635
2636 /* Decide which delay_config option we're using */
2637 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2638 if (match_netid(delay_conf->netid, netid, 0))
2639 break;
2640
2641 if (!delay_conf)
2642 /* No match, look for one without a netid */
2643 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2644 if (match_netid(delay_conf->netid, netid, 1))
2645 break;
2646
2647 if (delay_conf)
2648 {
2649 if (!option_bool(OPT_QUIET_DHCP))
2650 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2651 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2652 }
2653}
2654
Simon Kelley7622fc02009-06-04 20:32:05 +01002655#endif
2656
2657
2658
2659
2660
2661
2662