blob: 3f5075507b9ca07acc07511543b0fc2c8e609f94 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 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,
Wang Shanker4ded9622020-12-04 10:17:35 +080033 int opt, const 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,
Wang Shanker4ded9622020-12-04 10:17:35 +080057 unsigned short fuzz,
58 const char *pxevendor);
Simon Kelley7622fc02009-06-04 20:32:05 +010059
Simon Kelley9009d742008-11-14 20:04:27 +000060
Simon Kelley6b010842007-02-12 20:32:07 +000061static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Rosen Penev50a28412017-06-27 22:27:02 +010062static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
Wang Shanker4ded9622020-12-04 10:17:35 +080063static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor);
Simon Kelley7622fc02009-06-04 20:32:05 +010064static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000065static 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 +010066struct dhcp_boot *find_boot(struct dhcp_netid *netid);
Simon Kelleyfe71bba2016-05-14 20:50:45 +010067static 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 +010068static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
Wang Shanker4ded9622020-12-04 10:17:35 +080069static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor);
Floris Bos503c6092017-04-09 23:07:13 +010070
Simon Kelley824af852008-02-12 20:43:05 +000071size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelleyc7be0162017-05-10 22:21:53 +010072 size_t sz, time_t now, int unicast_dest, int loopback,
73 int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
Simon Kelley33820b72004-04-03 21:10:00 +010074{
Simon Kelley26128d22004-11-14 16:43:54 +000075 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000076 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010077 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000078 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000079 struct dhcp_netid_list *id_list;
Simon Kelley734d5312018-03-23 23:09:53 +000080 int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1;
Wang Shanker4ded9622020-12-04 10:17:35 +080081 const char *pxevendor = NULL;
Simon Kelley824af852008-02-12 20:43:05 +000082 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000083 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010084 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000085 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000086 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010087 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000088 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000089 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000090 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010091 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010092 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010093 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010094 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010095 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010096 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000097 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010098 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000099 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +0000100 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +0100101 unsigned char pxe_uuid[17];
Vladislav Grishenko99e88912013-11-26 11:02:29 +0000102 unsigned char *oui = NULL, *serial = NULL;
103#ifdef HAVE_SCRIPT
104 unsigned char *class = NULL;
105#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100106
Simon Kelley1a6bca82008-07-11 11:11:42 +0100107 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000108
109 /* set tag with name == interface */
110 iface_id.net = iface_name;
111 iface_id.next = NULL;
112 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100113
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100114 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100115 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000116
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100117 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000118 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119
Simon Kelley3be34542004-09-11 19:12:13 +0100120 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000121 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000122 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100123 u32 cookie = htonl(DHCP_COOKIE);
124
Simon Kelley3be34542004-09-11 19:12:13 +0100125 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100126 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100127 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100128
129 mess_type = option_uint(opt, 0, 1);
130
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100131 /* two things to note here: expand_buf may move the packet,
132 so reassign mess from daemon->packet. Also, the size
133 sent includes the IP and UDP headers, hence the magic "-28" */
134 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
135 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100136 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137
138 if (size > DHCP_PACKET_MAX)
139 size = DHCP_PACKET_MAX;
140 else if (size < sizeof(struct dhcp_packet))
141 size = sizeof(struct dhcp_packet);
142
143 if (expand_buf(&daemon->dhcp_packet, size))
144 {
Simon Kelley824af852008-02-12 20:43:05 +0000145 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100146 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100147 }
148 }
149
Simon Kelley3be34542004-09-11 19:12:13 +0100150 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
151 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000152 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100153 mess->ciaddr.s_addr = 0;
154
Simon Kelley316e2732010-01-22 20:16:09 +0000155 /* search for device identity from CPEWAN devices, we pass this through to the script */
156 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
157 {
158 unsigned int elen, offset, len = option_len(opt);
159
160 for (offset = 0; offset < (len - 5); offset += elen + 5)
161 {
162 elen = option_uint(opt, offset + 4 , 1);
Simon Kelley6a0b00f2017-09-25 20:19:55 +0100163 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
Simon Kelley316e2732010-01-22 20:16:09 +0000164 {
165 unsigned char *x = option_ptr(opt, offset + 5);
166 unsigned char *y = option_ptr(opt, offset + elen + 5);
167 oui = option_find1(x, y, 1, 1);
168 serial = option_find1(x, y, 2, 1);
Vladislav Grishenko99e88912013-11-26 11:02:29 +0000169#ifdef HAVE_SCRIPT
170 class = option_find1(x, y, 3, 1);
171#endif
Simon Kelley316e2732010-01-22 20:16:09 +0000172 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
173 the gateway id back. Note that the device class is optional */
174 if (oui && serial)
175 {
176 cpewan_id.net = "cpewan-id";
177 cpewan_id.next = netid;
178 netid = &cpewan_id;
179 }
180 break;
181 }
182 }
183 }
184
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100185 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
186 {
187 /* Any agent-id needs to be copied back out, verbatim, as the last option
188 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
189 get overwritten, then it will be shuffled back at the end of processing.
190 Note that the incoming options must not be overwritten here, so there has to
191 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100192 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100193 unsigned int total = option_len(opt) + 2;
Simon Kelley591ed1e2016-07-11 18:18:42 +0100194 unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz,
195 OPTION_END, 0);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100196 if (last_opt && last_opt < end - total)
197 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100198 end -= total;
199 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100200 memcpy(agent_id, opt, total);
201 }
202
203 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100204 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 +0100205 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100206
207 /* look for RFC5107 server-identifier-override */
208 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
209 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100210
211 /* if a circuit-id or remote-is option is provided, exact-match to options. */
212 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
213 {
214 int search;
215
216 if (vendor->match_type == MATCH_CIRCUIT)
217 search = SUBOPT_CIRCUIT_ID;
218 else if (vendor->match_type == MATCH_REMOTE)
219 search = SUBOPT_REMOTE_ID;
220 else if (vendor->match_type == MATCH_SUBSCRIBER)
221 search = SUBOPT_SUBSCR_ID;
222 else
223 continue;
224
Simon Kelley1a6bca82008-07-11 11:11:42 +0100225 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100226 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100227 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100228 {
229 vendor->netid.next = netid;
230 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100231 }
232 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100233 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100234
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100235 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
236 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100237 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000238
239 /* If there is no client identifier option, use the hardware address */
Florent Fourcot13a58f92019-06-20 10:26:40 +0200240 if (!option_bool(OPT_IGNORE_CLID) && (opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000241 {
Simon Kelley26128d22004-11-14 16:43:54 +0000242 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100243 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000244 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000245
Simon Kelley0a852542005-03-23 20:28:59 +0000246 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100247 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000248
249 /* If this request is missing a clid, but we've seen one before,
250 use it again for option matching etc. */
251 if (lease && !clid && lease->clid)
252 {
253 clid_len = lease->clid_len;
254 clid = lease->clid;
255 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000256
257 /* find mac to use for logging and hashing */
258 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000259 }
Simon Kelley3be34542004-09-11 19:12:13 +0100260
Simon Kelleycdeda282006-03-16 20:16:06 +0000261 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
262 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100263 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
264 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000265 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100266 mac->netid.next = netid;
267 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000268 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100269
Simon Kelley0a852542005-03-23 20:28:59 +0000270 /* Determine network for this packet. Our caller will have already linked all the
271 contexts which match the addresses of the receiving interface but if the
272 machine has an address already, or came via a relay, or we have a subnet selector,
273 we search again. If we don't have have a giaddr or explicit subnet selector,
274 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100275 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
276 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000277 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
278 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100279 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000280 struct shared_network *share = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100281 struct in_addr addr;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000282 int force = 0, via_relay = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000283
Simon Kelley3d8df262005-08-29 12:19:27 +0100284 if (subnet_addr.s_addr)
285 {
286 addr = subnet_addr;
287 force = 1;
288 }
289 else if (mess->giaddr.s_addr)
290 {
291 addr = mess->giaddr;
292 force = 1;
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000293 via_relay = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100294 }
Simon Kelley16972692006-10-16 20:04:18 +0100295 else
296 {
297 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
298 addr = mess->ciaddr;
299 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
300 if (context_tmp->netmask.s_addr &&
301 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
302 is_same_net(addr, context_tmp->end, context_tmp->netmask))
303 {
304 context_new = context;
305 break;
306 }
307 }
308
309 if (!context_new)
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000310 {
311 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
312 {
313 struct in_addr netmask = context_tmp->netmask;
314
315 /* guess the netmask for relayed networks */
316 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
317 {
318 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
319 netmask.s_addr = htonl(0xff000000);
320 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
321 netmask.s_addr = htonl(0xffff0000);
322 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
323 netmask.s_addr = htonl(0xffffff00);
324 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100325
Simon Kelleyae5b7e02019-03-27 22:33:28 +0000326 /* check to see is a context is OK because of a shared address on
327 the relayed subnet. */
328 if (via_relay)
329 for (share = daemon->shared_networks; share; share = share->next)
330 {
331#ifdef HAVE_DHCP6
332 if (share->shared_addr.s_addr == 0)
333 continue;
334#endif
335 if (share->if_index != 0 ||
336 share->match_addr.s_addr != mess->giaddr.s_addr)
337 continue;
338
339 if (netmask.s_addr != 0 &&
340 is_same_net(share->shared_addr, context_tmp->start, netmask) &&
341 is_same_net(share->shared_addr, context_tmp->end, netmask))
342 break;
343 }
344
345 /* This section fills in context mainly when a client which is on a remote (relayed)
346 network renews a lease without using the relay, after dnsmasq has restarted. */
347 if (share ||
348 (netmask.s_addr != 0 &&
349 is_same_net(addr, context_tmp->start, netmask) &&
350 is_same_net(addr, context_tmp->end, netmask)))
351 {
352 context_tmp->netmask = netmask;
353 if (context_tmp->local.s_addr == 0)
354 context_tmp->local = fallback;
355 if (context_tmp->router.s_addr == 0 && !share)
356 context_tmp->router = mess->giaddr;
357
358 /* fill in missing broadcast addresses for relayed ranges */
359 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
360 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
361
362 context_tmp->current = context_new;
363 context_new = context_tmp;
364 }
365
366 }
367 }
368
Simon Kelley3d8df262005-08-29 12:19:27 +0100369 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100370 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000371 }
Simon Kelley3be34542004-09-11 19:12:13 +0100372
373 if (!context)
374 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100375 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100376 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
377 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 +0100378 return 0;
379 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100380
Simon Kelley28866e92011-02-14 20:19:14 +0000381 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100382 {
383 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100384 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
385 {
386 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100387 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100388 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100389 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100390 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100391 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100392 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100393 }
394 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100395
396 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
397 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000398 If no data given, existence of the option is enough. This code handles
Simon Kelley86e92f92013-04-23 11:31:39 +0100399 rfc3925 V-I classes too. */
400 for (o = daemon->dhcp_match; o; o = o->next)
401 {
402 unsigned int len, elen, match = 0;
403 size_t offset, o2;
404
405 if (o->flags & DHOPT_RFC3925)
406 {
407 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
408 continue;
409
410 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
411 {
412 len = option_uint(opt, offset + 4 , 1);
413 /* Need to take care that bad data can't run us off the end of the packet */
Matthias Andree9828ab12017-05-21 22:41:16 +0100414 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100415 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
416 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
417 {
418 elen = option_uint(opt, o2, 1);
Simon Kelleyfc193992020-03-05 22:13:45 +0000419 if ((o2 + elen + 1 <= (unsigned)option_len(opt)) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100420 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
421 break;
422 }
423 if (match)
424 break;
425 }
426 }
427 else
428 {
429 if (!(opt = option_find(mess, sz, o->opt, 1)))
430 continue;
431
432 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
433 }
434
435 if (match)
436 {
437 o->netid->next = netid;
438 netid = o->netid;
439 }
440 }
441
442 /* user-class options are, according to RFC3004, supposed to contain
443 a set of counted strings. Here we check that this is so (by seeing
444 if the counts are consistent with the overall option length) and if
445 so zero the counts so that we don't get spurious matches between
446 the vendor string and the counts. If the lengths don't add up, we
447 assume that the option is a single string and non RFC3004 compliant
448 and just do the substring match. dhclient provides these broken options.
449 The code, later, which sends user-class data to the lease-change script
450 relies on the transformation done here.
451 */
452
453 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
454 {
455 unsigned char *ucp = option_ptr(opt, 0);
456 int tmp, j;
457 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
458 if (j == option_len(opt))
459 for (j = 0; j < option_len(opt); j = tmp)
460 {
461 tmp = j + ucp[j] + 1;
462 ucp[j] = 0;
463 }
464 }
465
466 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
467 {
468 int mopt;
469
470 if (vendor->match_type == MATCH_VENDOR)
471 mopt = OPTION_VENDOR_ID;
472 else if (vendor->match_type == MATCH_USER)
473 mopt = OPTION_USER_CLASS;
474 else
475 continue;
476
477 if ((opt = option_find(mess, sz, mopt, 1)))
478 {
479 int i;
480 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
481 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
482 {
483 vendor->netid.next = netid;
484 netid = &vendor->netid;
485 break;
486 }
487 }
488 }
489
490 /* mark vendor-encapsulated options which match the client-supplied vendor class,
491 save client-supplied vendor class */
492 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
493 {
494 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
495 vendor_class_len = option_len(opt);
496 }
497 match_vendor_opts(opt, daemon->dhcp_opts);
498
499 if (option_bool(OPT_LOG_OPTS))
500 {
501 if (sanitise(opt, daemon->namebuff))
502 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
503 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
504 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
505 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100506
Simon Kelley3be34542004-09-11 19:12:13 +0100507 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100508
Simon Kelleycdeda282006-03-16 20:16:06 +0000509 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
Simon Kelley52ec7832020-02-07 21:05:54 +0000510 mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100511
512 /* set "known" tag for known hosts */
513 if (config)
514 {
515 known_id.net = "known";
516 known_id.next = netid;
517 netid = &known_id;
518 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100519 else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
Simon Kelley52ec7832020-02-07 21:05:54 +0000520 mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)))
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100521 {
522 known_id.net = "known-othernet";
523 known_id.next = netid;
524 netid = &known_id;
525 }
Simon Kelley26128d22004-11-14 16:43:54 +0000526
Simon Kelley316e2732010-01-22 20:16:09 +0000527 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100528 {
529 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000530 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000531 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100532
533 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100534 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100535 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000536
Simon Kelley26128d22004-11-14 16:43:54 +0000537 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000538 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000539
540 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
541
542 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000543 {
544 hostname = config->hostname;
545 domain = config->domain;
546 }
547
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100548 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000549 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100550 struct dhcp_netid_list *list;
551
552 for (list = config->netid; list; list = list->next)
553 {
554 list->list->next = netid;
555 netid = list->list;
556 }
Simon Kelley26128d22004-11-14 16:43:54 +0000557 }
558
559 /* Match incoming filename field as a netid. */
560 if (mess->file[0])
561 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000562 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
563 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
564 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000565 id.next = netid;
566 netid = &id;
567 }
Simon Kelley6b010842007-02-12 20:32:07 +0000568
569 /* Add "bootp" as a tag to allow different options, address ranges etc
570 for BOOTP clients */
571 bootp_id.net = "bootp";
572 bootp_id.next = netid;
573 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000574
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100575 tagif_netid = run_tag_if(netid);
576
Simon Kelley26128d22004-11-14 16:43:54 +0000577 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100578 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100579 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000580
Simon Kelley3d8df262005-08-29 12:19:27 +0100581 if (!message)
582 {
Simon Kelley9009d742008-11-14 20:04:27 +0000583 int nailed = 0;
584
Simon Kelley3d8df262005-08-29 12:19:27 +0100585 if (have_config(config, CONFIG_ADDR))
586 {
Simon Kelley9009d742008-11-14 20:04:27 +0000587 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100588 logaddr = &config->addr;
589 mess->yiaddr = config->addr;
590 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000591 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100592 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000593 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000594 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100595 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100596 else
597 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000598 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100599 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000600 {
601 if (lease)
602 {
603 /* lease exists, wrong network. */
604 lease_prune(lease, now);
605 lease = NULL;
606 }
Simon Kelleyc7be0162017-05-10 22:21:53 +0100607 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
Simon Kelleye17fb622006-01-14 20:33:46 +0000608 message = _("no address available");
609 }
610 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100611 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100612 }
613
Simon Kelley9009d742008-11-14 20:04:27 +0000614 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
615 message = _("wrong network");
616 else if (context->netid.net)
617 {
618 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100619 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100620 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100621
Simon Kelley4cb1b322012-02-06 14:30:41 +0000622 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100623
Simon Kelley9009d742008-11-14 20:04:27 +0000624 if (!message && !nailed)
625 {
626 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100627 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000628 break;
629 if (!id_list)
630 message = _("no address configured");
631 }
632
Simon Kelley7cebd202006-05-06 14:13:33 +0100633 if (!message &&
634 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000635 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000636 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000637
Simon Kelley3d8df262005-08-29 12:19:27 +0100638 if (!message)
639 {
640 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100641
Simon Kelleya9ab7322012-04-28 11:29:37 +0100642 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100643 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000644 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100645 /* infinite lease unless nailed in dhcp-host line. */
646 lease_set_expires(lease,
647 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
648 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000649 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100650
Simon Kelleya9df0e32017-04-28 22:43:00 +0100651 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000652 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +0800653 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100654 }
655 }
656
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100657 daemon->metrics[METRIC_BOOTP]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100658 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000659
Simon Kelley7de060b2011-08-26 17:24:52 +0100660 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100661 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000662
Roy Marples3f3adae2013-07-25 16:22:46 +0100663 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100664 {
665 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
666 int len = option_len(opt);
667 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100668 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100669
Simon Kelley9fed0f72012-08-30 11:43:35 +0100670 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100671 len -= 3;
672 op += 3;
673 pp = op;
674
Simon Kelley9fed0f72012-08-30 11:43:35 +0100675 /* NB, the following always sets at least one bit */
676 if (option_bool(OPT_FQDN_UPDATE))
677 {
678 if (fqdn_flags & 0x01)
679 {
680 fqdn_flags |= 0x02; /* set O */
681 fqdn_flags &= ~0x01; /* clear S */
682 }
683 fqdn_flags |= 0x08; /* set N */
684 }
685 else
686 {
687 if (!(fqdn_flags & 0x01))
688 fqdn_flags |= 0x03; /* set S and O */
689 fqdn_flags &= ~0x08; /* clear N */
690 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100691
692 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100693 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100694 {
695 memcpy(pq, op+1, *op);
696 pq += *op;
697 op += (*op)+1;
698 *(pq++) = '.';
699 }
700 else
701 {
702 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000703 if (len > 0 && op[len-1] == 0)
704 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100705 pq += len + 1;
706 }
707
708 if (pq != daemon->dhcp_buff)
709 pq--;
710
711 *pq = 0;
712
Simon Kelley1f15b812009-10-13 17:49:32 +0100713 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100714 offer_hostname = client_hostname = daemon->dhcp_buff;
715 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000716 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000717 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000718 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100719 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000720 /* Microsoft clients are broken, and need zero-terminated strings
721 in options. We detect this state here, and do the same in
722 any options we send */
723 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
724 borken_opt = 1;
725 else
726 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100727 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100728 client_hostname = daemon->dhcp_buff;
729 }
730
Simon Kelley34d41472019-12-05 23:44:29 +0000731 if (client_hostname)
732 {
733 struct dhcp_match_name *m;
734 size_t nl = strlen(client_hostname);
735
736 if (option_bool(OPT_LOG_OPTS))
737 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
738 for (m = daemon->dhcp_name_match; m; m = m->next)
739 {
740 size_t ml = strlen(m->name);
741 char save = 0;
742
743 if (nl < ml)
744 continue;
745 if (nl > ml)
746 {
747 save = client_hostname[ml];
748 client_hostname[ml] = 0;
749 }
750
751 if (hostname_isequal(client_hostname, m->name) &&
752 (save == 0 || m->wildcard))
753 {
754 m->netid->next = netid;
755 netid = m->netid;
756 }
757
758 if (save != 0)
759 client_hostname[ml] = save;
760 }
761 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100762
Simon Kelley3d8df262005-08-29 12:19:27 +0100763 if (have_config(config, CONFIG_NAME))
764 {
765 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000766 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000767 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000768 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100769 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000770 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100771 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100772 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100773 {
Simon Kelley9009d742008-11-14 20:04:27 +0000774 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100775
Simon Kelley6ebdc952019-10-30 21:04:27 +0000776 if (strlen(client_hostname) != 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100777 {
778 hostname = client_hostname;
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100779
Simon Kelley5aabfc72007-08-29 11:24:47 +0100780 if (!config)
781 {
782 /* Search again now we have a hostname.
783 Only accept configs without CLID and HWADDR here, (they won't match)
784 to avoid impersonation by name. */
785 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
786 mess->chaddr, mess->hlen,
Simon Kelley52ec7832020-02-07 21:05:54 +0000787 mess->htype, hostname, run_tag_if(netid));
Simon Kelley9009d742008-11-14 20:04:27 +0000788 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000789 {
790 config = new;
791 /* set "known" tag for known hosts */
792 known_id.net = "known";
793 known_id.next = netid;
794 netid = &known_id;
795 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100796 }
Simon Kelley6ebdc952019-10-30 21:04:27 +0000797 }
798 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100799
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100800 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100801 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100802 struct dhcp_netid_list *list;
803
804 for (list = config->netid; list; list = list->next)
805 {
806 list->list->next = netid;
807 netid = list->list;
808 }
Simon Kelleya2226412004-05-13 20:27:08 +0100809 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100810
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100811 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100812
Simon Kelley26128d22004-11-14 16:43:54 +0000813 /* if all the netids in the ignore list are present, ignore this client */
814 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100815 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000816 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100817
818 /* If configured, we can override the server-id to be the address of the relay,
819 so that all traffic goes via the relay and can pick up agent-id info. This can be
820 configured for all relays, or by address. */
821 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
822 {
823 if (!daemon->override_relays)
824 override = mess->giaddr;
825 else
826 {
827 struct addr_list *l;
828 for (l = daemon->override_relays; l; l = l->next)
829 if (l->addr.s_addr == mess->giaddr.s_addr)
830 break;
831 if (l)
832 override = mess->giaddr;
833 }
834 }
835
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100836 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
837 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000838 clid = NULL;
839
Simon Kelley7622fc02009-06-04 20:32:05 +0100840 /* Check if client is PXE client. */
Wang Shanker4ded9622020-12-04 10:17:35 +0800841 if (daemon->enable_pxe &&
842 is_pxe_client(mess, sz, &pxevendor))
Simon Kelley7622fc02009-06-04 20:32:05 +0100843 {
844 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
845 {
846 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
847 uuid = pxe_uuid;
848 }
849
850 /* Check if this is really a PXE bootserver request, and handle specially if so. */
851 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
852 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
853 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
854 {
855 struct pxe_service *service;
856 int type = option_uint(opt, 0, 2);
857 int layer = option_uint(opt, 2, 2);
858 unsigned char save71[4];
859 struct dhcp_opt opt71;
860
Simon Kelley1f15b812009-10-13 17:49:32 +0100861 if (ignore)
862 return 0;
863
Simon Kelley7622fc02009-06-04 20:32:05 +0100864 if (layer & 0x8000)
865 {
866 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
867 return 0;
868 }
869
870 memcpy(save71, option_ptr(opt, 0), 4);
871
872 for (service = daemon->pxe_services; service; service = service->next)
873 if (service->type == type)
874 break;
875
Simon Kelley549b1a42015-05-20 20:20:24 +0100876 for (; context; context = context->current)
877 if (match_netid(context->filter, tagif_netid, 1) &&
878 is_same_net(mess->ciaddr, context->start, context->netmask))
879 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100880
Simon Kelley549b1a42015-05-20 20:20:24 +0100881 if (!service || !service->basename || !context)
882 return 0;
883
Simon Kelleya9df0e32017-04-28 22:43:00 +0100884 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100885
886 mess->yiaddr = mess->ciaddr;
887 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000888 if (service->sname)
889 mess->siaddr = a_record_from_hosts(service->sname, now);
890 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100891 mess->siaddr = service->server;
892 else
893 mess->siaddr = context->local;
894
Matthias Andreef77700a2017-05-21 22:36:09 +0100895 if (strchr(service->basename, '.'))
896 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100897 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100898 else
899 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100900 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100901
Simon Kelley7622fc02009-06-04 20:32:05 +0100902 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
903 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
Wang Shanker4ded9622020-12-04 10:17:35 +0800904 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelley7622fc02009-06-04 20:32:05 +0100905
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100906 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100907 opt71.val = save71;
908 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
909 opt71.len = 4;
910 opt71.flags = DHOPT_VENDOR_MATCH;
911 opt71.netid = NULL;
912 opt71.next = daemon->dhcp_opts;
913 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
914
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100915 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000916 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100917 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100918 }
919
920 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
921 {
922 pxearch = option_uint(opt, 0, 2);
923
Simon Kelley316e2732010-01-22 20:16:09 +0000924 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000925 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100926 {
Simon Kelley28866e92011-02-14 20:19:14 +0000927 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100928 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100929
Simon Kelley28866e92011-02-14 20:19:14 +0000930 for (tmp = context; tmp; tmp = tmp->current)
931 if ((tmp->flags & CONTEXT_PROXY) &&
932 match_netid(tmp->filter, tagif_netid, 1))
933 break;
934
935 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100936 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100937 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100938 int redirect4011 = 0;
939
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100940 if (tmp->netid.net)
941 {
942 tmp->netid.next = netid;
943 tagif_netid = run_tag_if(&tmp->netid);
944 }
945
946 boot = find_boot(tagif_netid);
947
Simon Kelley28866e92011-02-14 20:19:14 +0000948 mess->yiaddr.s_addr = 0;
949 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
950 {
951 mess->ciaddr.s_addr = 0;
952 mess->flags |= htons(0x8000); /* broadcast */
953 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100954
Simon Kelleya9df0e32017-04-28 22:43:00 +0100955 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000956
Simon Kelley0a4a0492016-05-15 20:13:45 +0100957 /* Redirect EFI clients to port 4011 */
958 if (pxearch >= 6)
959 {
960 redirect4011 = 1;
961 mess->siaddr = tmp->local;
962 }
963
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100964 /* Returns true if only one matching service is available. On port 4011,
965 it also inserts the boot file and server name. */
966 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100967
968 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000969 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000970 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100971 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100972 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000973 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100974 else if (boot->tftp_sname)
975 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000976
977 if (boot->file)
Petr Menšík47b45b22018-08-15 18:17:00 +0200978 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley28866e92011-02-14 20:19:14 +0000979 }
980
981 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
982 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100983 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Wang Shanker4ded9622020-12-04 10:17:35 +0800984 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelley28866e92011-02-14 20:19:14 +0000985 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +0100986 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +0100987 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
988
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100989 daemon->metrics[METRIC_PXE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100990 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000991 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +0100992 if (!ignore)
993 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100994 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100995 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100996 }
997 }
998 }
999
1000 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +00001001 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +01001002 return 0;
1003
Simon Kelleybb01cb92004-12-13 20:56:23 +00001004 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001005 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001006 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001007 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +00001008 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001009 }
1010
Simon Kelley3be34542004-09-11 19:12:13 +01001011 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001012 {
Simon Kelley44a2a312004-03-10 20:04:35 +00001013 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +00001014 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001015 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001016 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001017
Simon Kelley44a2a312004-03-10 20:04:35 +00001018 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001019 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +00001020
Simon Kelleybb01cb92004-12-13 20:56:23 +00001021 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +00001022 return 0;
1023
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001024 daemon->metrics[METRIC_DHCPDECLINE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001025 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 +00001026
1027 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
1028 lease_prune(lease, now);
1029
Simon Kelley33820b72004-04-03 21:10:00 +01001030 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +00001031 config->addr.s_addr == option_addr(opt).s_addr)
1032 {
Simon Kelley849a8352006-06-09 21:02:31 +01001033 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +01001034 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001035 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +01001036 config->flags |= CONFIG_DECLINED;
1037 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +00001038 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001039 else
1040 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +01001041 for (; context; context = context->current)
1042 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +00001043
1044 return 0;
1045
1046 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001047 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +01001048 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001049 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001050 return 0;
1051
Simon Kelley44a2a312004-03-10 20:04:35 +00001052 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
1053 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001054 else
Simon Kelleyb8187c82005-11-26 21:46:27 +00001055 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001056
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001057 daemon->metrics[METRIC_DHCPRELEASE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001058 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001059
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001060 return 0;
1061
1062 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001063 if (ignore || have_config(config, CONFIG_DISABLE))
1064 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001065 if (option_bool(OPT_QUIET_DHCP))
1066 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001067 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001068 opt = NULL;
1069 }
1070 else
1071 {
1072 struct in_addr addr, conf;
1073
Simon Kelley1a6bca82008-07-11 11:11:42 +01001074 addr.s_addr = conf.s_addr = 0;
1075
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001076 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1077 addr = option_addr(opt);
1078
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001079 if (have_config(config, CONFIG_ADDR))
1080 {
Simon Kelley849a8352006-06-09 21:02:31 +01001081 char *addrs = inet_ntoa(config->addr);
1082
Simon Kelley9009d742008-11-14 20:04:27 +00001083 if ((ltmp = lease_find_by_addr(config->addr)) &&
1084 ltmp != lease &&
1085 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001086 {
1087 int len;
1088 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1089 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001090 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +01001091 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001092 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001093 else
1094 {
1095 struct dhcp_context *tmp;
1096 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001097 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001098 break;
1099 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001100 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 +01001101 else if (have_config(config, CONFIG_DECLINED) &&
1102 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001103 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001104 else
1105 conf = config->addr;
1106 }
1107 }
1108
1109 if (conf.s_addr)
1110 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001111 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001112 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001113 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001114 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001115 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001116 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001117 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001118 else if (emac_len == 0)
1119 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001120 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001121 message = _("no address available");
1122 }
1123
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001124 daemon->metrics[METRIC_DHCPDISCOVER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001125 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 +01001126
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001127 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001128 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001129
Simon Kelleycdeda282006-03-16 20:16:06 +00001130 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001131 {
1132 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001133 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001134 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001135
Simon Kelley4cb1b322012-02-06 14:30:41 +00001136 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001137 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001138
1139 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1140 {
1141 rapid_commit = 1;
1142 goto rapid_commit;
1143 }
1144
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001145 daemon->metrics[METRIC_DHCPOFFER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001146 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001147
Simon Kelley824af852008-02-12 20:43:05 +00001148 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001149 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001150 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001151 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001152 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001153 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001154 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001155 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001156
Simon Kelley7de060b2011-08-26 17:24:52 +01001157 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001158
1159
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001160 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001161 if (ignore || have_config(config, CONFIG_DISABLE))
1162 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001163 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001164 {
1165 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001166 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001167
Simon Kelley4011c4e2006-10-28 16:26:19 +01001168 /* send vendor and user class info for new or recreated lease */
1169 do_classes = 1;
1170
Simon Kelleybb01cb92004-12-13 20:56:23 +00001171 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001172 {
Simon Kelley3be34542004-09-11 19:12:13 +01001173 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001174 selecting = 1;
1175
Simon Kelley1a6bca82008-07-11 11:11:42 +01001176 if (override.s_addr != 0)
1177 {
1178 if (option_addr(opt).s_addr != override.s_addr)
1179 return 0;
1180 }
Simon Kelley9009d742008-11-14 20:04:27 +00001181 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001182 {
1183 for (; context; context = context->current)
1184 if (context->local.s_addr == option_addr(opt).s_addr)
1185 break;
1186
1187 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001188 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001189 /* Handle very strange configs where clients have more than one route to the server.
1190 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1191 Have to set override to make sure we echo back the correct server-id */
1192 struct irec *intr;
1193
Simon Kelley115ac3e2013-05-20 11:28:32 +01001194 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001195
1196 for (intr = daemon->interfaces; intr; intr = intr->next)
1197 if (intr->addr.sa.sa_family == AF_INET &&
1198 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1199 intr->tftp_ok)
1200 break;
1201
1202 if (intr)
1203 override = intr->addr.in.sin_addr;
1204 else
1205 {
1206 /* In auth mode, a REQUEST sent to the wrong server
1207 should be faulted, so that the client establishes
1208 communication with us, otherwise, silently ignore. */
1209 if (!option_bool(OPT_AUTHORITATIVE))
1210 return 0;
1211 message = _("wrong server-ID");
1212 }
Simon Kelley9009d742008-11-14 20:04:27 +00001213 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001214 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001215
Simon Kelley3be34542004-09-11 19:12:13 +01001216 /* If a lease exists for this host and another address, squash it. */
1217 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1218 {
1219 lease_prune(lease, now);
1220 lease = NULL;
1221 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001222 }
Simon Kelley3be34542004-09-11 19:12:13 +01001223 else
1224 {
1225 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001226 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001227 return 0;
1228
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001229 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001230 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001231 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001232 }
1233 else
1234 {
1235 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001236 /* Check existing lease for this address.
1237 We allow it to be missing if dhcp-authoritative mode
1238 as long as we can allocate the lease now - checked below.
1239 This makes for a smooth recovery from a lost lease DB */
1240 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001241 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001242 {
Simon Kelley28866e92011-02-14 20:19:14 +00001243 /* A client rebinding will broadcast the request, so we may see it even
1244 if the lease is held by another server. Just ignore it in that case.
1245 If the request is unicast to us, then somethings wrong, NAK */
1246 if (!unicast_dest)
1247 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001248 message = _("lease not found");
1249 /* ensure we broadcast NAK */
1250 unicast_dest = 0;
1251 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001252
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001253 /* desynchronise renewals */
1254 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001255 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001256 }
Simon Kelley734d5312018-03-23 23:09:53 +00001257
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001258 daemon->metrics[METRIC_DHCPREQUEST]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001259 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001260
1261 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001262 if (!message)
1263 {
1264 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001265 struct dhcp_context *tmp = NULL;
1266
1267 if (have_config(config, CONFIG_ADDR))
1268 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001269 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001270 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001271
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001272 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001273 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001274 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001275 message = _("wrong network");
1276 /* ensure we broadcast NAK */
1277 unicast_dest = 0;
1278 }
1279
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001280 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001281 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001282 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001283 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001284
Simon Kelleydfa666f2004-08-02 18:27:27 +01001285 /* Check if a new static address has been configured. Be very sure that
1286 when the client does DISCOVER, it will get the static address, otherwise
1287 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001288 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001289 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001290 (!have_config(config, CONFIG_DECLINED) ||
1291 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001292 config->addr.s_addr != mess->yiaddr.s_addr &&
1293 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001294 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001295
1296 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001297 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001298 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001299
Simon Kelley9009d742008-11-14 20:04:27 +00001300 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1301 {
1302 /* If a host is configured with more than one MAC address, it's OK to 'nix
1303 a lease from one of it's MACs to give the address to another. */
1304 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1305 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001306 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001307 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1308 inet_ntoa(ltmp->addr));
1309 lease = ltmp;
1310 }
Simon Kelley16972692006-10-16 20:04:18 +01001311 else
Simon Kelley9009d742008-11-14 20:04:27 +00001312 message = _("address in use");
1313 }
1314
1315 if (!message)
1316 {
1317 if (emac_len == 0)
1318 message = _("no unique-id");
1319
1320 else if (!lease)
1321 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001322 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001323 do_classes = 1;
1324 else
1325 message = _("no leases left");
1326 }
Simon Kelley16972692006-10-16 20:04:18 +01001327 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001328 }
Simon Kelley16972692006-10-16 20:04:18 +01001329
Simon Kelley44a2a312004-03-10 20:04:35 +00001330 if (message)
1331 {
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001332 daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
Simon Kelley734d5312018-03-23 23:09:53 +00001333 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1334
1335 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1336 if (rapid_commit)
1337 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001338
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001339 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001340 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001341 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001342 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001343 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001344 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1345 a distant subnet which unicast a REQ to us won't work. */
1346 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1347 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1348 {
1349 mess->flags |= htons(0x8000); /* broadcast */
1350 mess->ciaddr.s_addr = 0;
1351 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001352 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001353 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001354 {
Simon Kelley316e2732010-01-22 20:16:09 +00001355 if (context->netid.net)
1356 {
1357 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001358 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001359 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001360
Simon Kelley4cb1b322012-02-06 14:30:41 +00001361 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001362
Simon Kelleydcffad22012-04-24 15:25:18 +01001363 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001364 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001365 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001366 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001367
Simon Kelleydcffad22012-04-24 15:25:18 +01001368#ifdef HAVE_SCRIPT
1369 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001370 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001371 struct dhcp_netid *n;
1372
1373 if (mess->giaddr.s_addr)
1374 lease->giaddr = mess->giaddr;
1375
1376 free(lease->extradata);
1377 lease->extradata = NULL;
1378 lease->extradata_size = lease->extradata_len = 0;
1379
1380 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1381 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1382 add_extradata_opt(lease, oui);
1383 add_extradata_opt(lease, serial);
1384 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001385
1386 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1387 {
1388 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1389 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1390 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1391 }
1392 else
1393 {
1394 add_extradata_opt(lease, NULL);
1395 add_extradata_opt(lease, NULL);
1396 add_extradata_opt(lease, NULL);
1397 }
1398
ZHAO Yuf89cae32016-12-22 22:32:31 +00001399 /* DNSMASQ_REQUESTED_OPTIONS */
1400 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1401 {
1402 int len = option_len(opt);
1403 unsigned char *rop = option_ptr(opt, 0);
1404 char *q = daemon->namebuff;
1405 int i;
1406 for (i = 0; i < len; i++)
1407 {
1408 q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
1409 }
1410 lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0);
1411 }
1412 else
1413 {
1414 add_extradata_opt(lease, NULL);
1415 }
1416
Simon Kelleydcffad22012-04-24 15:25:18 +01001417 /* space-concat tag set */
1418 if (!tagif_netid)
1419 add_extradata_opt(lease, NULL);
1420 else
1421 for (n = tagif_netid; n; n = n->next)
1422 {
1423 struct dhcp_netid *n1;
1424 /* kill dupes */
1425 for (n1 = n->next; n1; n1 = n1->next)
1426 if (strcmp(n->net, n1->net) == 0)
1427 break;
1428 if (!n1)
1429 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1430 }
1431
1432 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1433 {
1434 int len = option_len(opt);
1435 unsigned char *ucp = option_ptr(opt, 0);
1436 /* If the user-class option started as counted strings, the first byte will be zero. */
1437 if (len != 0 && ucp[0] == 0)
1438 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001439 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001440 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001441 }
Simon Kelley316e2732010-01-22 20:16:09 +00001442#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001443 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001444
1445 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1446 {
1447 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001448 hostname = client_hostname;
1449 hostname_auth = 1;
1450 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001451
Simon Kelley824af852008-02-12 20:43:05 +00001452 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001453 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001454
Simon Kelley832af0b2007-01-21 20:01:28 +00001455 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1456 if (!hostname_auth)
1457 {
1458 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001459 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001460 break;
1461 if (id_list)
1462 hostname = NULL;
1463 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001464
1465 /* Last ditch, if configured, generate hostname from mac address */
1466 if (!hostname && emac_len != 0)
1467 {
1468 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1469 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1470 break;
1471 if (id_list)
1472 {
1473 int i;
1474
1475 hostname = daemon->dhcp_buff;
1476 /* buffer is 256 bytes, 3 bytes per octet */
1477 for (i = 0; (i < emac_len) && (i < 80); i++)
1478 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1479 hostname = daemon->dhcp_buff;
1480 }
1481 }
1482
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001483 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001484 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001485
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001486 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001487 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001488
1489 if (override.s_addr != 0)
1490 lease->override = override;
1491 else
1492 override = lease->override;
1493
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001494 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001495 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001496
Simon Kelleya9df0e32017-04-28 22:43:00 +01001497 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001498 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001499 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001500 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001501 if (rapid_commit)
1502 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1503 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001504 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
Simon Kelley44a2a312004-03-10 20:04:35 +00001505 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001506
Simon Kelley7de060b2011-08-26 17:24:52 +01001507 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001508
1509 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001510 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001511 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001512
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001513 daemon->metrics[METRIC_DHCPINFORM]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001514 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001515
Simon Kelley73a08a22009-02-05 20:28:08 +00001516 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001517 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001518
1519 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001520 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001521
Simon Kelley5aabfc72007-08-29 11:24:47 +01001522 /* Find a least based on IP address if we didn't
1523 get one from MAC address/client-d */
1524 if (!lease &&
1525 (lease = lease_find_by_addr(mess->ciaddr)) &&
1526 lease->hostname)
1527 hostname = lease->hostname;
1528
Simon Kelley0f371f92013-07-27 15:15:38 +01001529 if (!hostname)
1530 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001531
Simon Kelley73a08a22009-02-05 20:28:08 +00001532 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001533 {
1534 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001535 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001536 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001537
Simon Kelley4cb1b322012-02-06 14:30:41 +00001538 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001539
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001540 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001541 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001542
Simon Kelley3927da42008-07-20 15:10:39 +01001543 if (lease)
1544 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001545 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001546 if (override.s_addr != 0)
1547 lease->override = override;
1548 else
1549 override = lease->override;
1550 }
1551
Simon Kelleya9df0e32017-04-28 22:43:00 +01001552 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001553 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001554 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001555
1556 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1557 we supply a utility which makes DHCPINFORM requests to get this information.
1558 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1559 which won't be true for ordinary clients, but will be true for the
1560 dhcp_lease_time utility. */
1561 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1562 {
1563 if (lease->expires == 0)
1564 time = 0xffffffff;
1565 else
1566 time = (unsigned int)difftime(lease->expires, now);
1567 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1568 }
1569
Simon Kelley9009d742008-11-14 20:04:27 +00001570 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001571 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001572
Simon Kelley5aabfc72007-08-29 11:24:47 +01001573 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001574 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001575 }
1576
1577 return 0;
1578}
1579
Simon Kelley6b010842007-02-12 20:32:07 +00001580/* find a good value to use as MAC address for logging and address-allocation hashing.
1581 This is normally just the chaddr field from the DHCP packet,
1582 but eg Firewire will have hlen == 0 and use the client-id instead.
1583 This could be anything, but will normally be EUI64 for Firewire.
1584 We assume that if the first byte of the client-id equals the htype byte
1585 then the client-id is using the usual encoding and use the rest of the
1586 client-id: if not we can use the whole client-id. This should give
1587 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001588unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001589 int clid_len, unsigned char *clid, int *len_out)
1590{
1591 if (hwlen == 0 && clid && clid_len > 3)
1592 {
1593 if (clid[0] == hwtype)
1594 {
1595 *len_out = clid_len - 1 ;
1596 return clid + 1;
1597 }
1598
1599#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1600 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1601 {
1602 *len_out = clid_len - 1 ;
1603 return clid + 1;
1604 }
1605#endif
1606
1607 *len_out = clid_len;
1608 return clid;
1609 }
1610
1611 *len_out = hwlen;
1612 return hwaddr;
1613}
1614
Simon Kelley824af852008-02-12 20:43:05 +00001615static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001616{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001617 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001618
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001619 if (opt)
1620 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001621 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001622 if (req_time < 120 )
1623 req_time = 120; /* sanity */
1624 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1625 time = req_time;
1626 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001627
1628 return time;
1629}
1630
Simon Kelley73a08a22009-02-05 20:28:08 +00001631static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001632{
Simon Kelley73a08a22009-02-05 20:28:08 +00001633 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001634 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001635 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001636 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001637 else
1638 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001639}
1640
Simon Kelleyf2621c72007-04-29 19:47:21 +01001641static int sanitise(unsigned char *opt, char *buf)
1642{
1643 char *p;
1644 int i;
1645
1646 *buf = 0;
1647
1648 if (!opt)
1649 return 0;
1650
Simon Kelley1a6bca82008-07-11 11:11:42 +01001651 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001652
1653 for (i = option_len(opt); i > 0; i--)
1654 {
1655 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001656 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001657 *buf++ = c;
1658 }
1659 *buf = 0; /* add terminator */
1660
1661 return 1;
1662}
1663
Simon Kelley316e2732010-01-22 20:16:09 +00001664#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001665static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1666{
1667 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001668 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001669 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001670 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001671}
1672#endif
1673
Simon Kelley5aabfc72007-08-29 11:24:47 +01001674static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001675 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001676{
Simon Kelley16972692006-10-16 20:04:18 +01001677 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001678
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001679 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001680 return;
1681
Simon Kelley16972692006-10-16 20:04:18 +01001682 /* addr may be misaligned */
1683 if (addr)
1684 memcpy(&a, addr, sizeof(a));
1685
Simon Kelley7622fc02009-06-04 20:32:05 +01001686 print_mac(daemon->namebuff, ext_mac, mac_len);
1687
Simon Kelley28866e92011-02-14 20:19:14 +00001688 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001689 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001690 ntohl(xid),
1691 type,
1692 interface,
1693 addr ? inet_ntoa(a) : "",
1694 addr ? " " : "",
1695 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001696 string ? string : "",
1697 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001698 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001699 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001700 type,
1701 interface,
1702 addr ? inet_ntoa(a) : "",
1703 addr ? " " : "",
1704 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001705 string ? string : "",
1706 err ? err : "");
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001707
1708#ifdef HAVE_UBUS
1709 if (!strcmp(type, "DHCPACK"))
1710 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1711 else if (!strcmp(type, "DHCPRELEASE"))
1712 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
1713#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001714}
1715
Simon Kelley7622fc02009-06-04 20:32:05 +01001716static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001717{
1718 while (*start != OPTION_END)
1719 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001720 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 +01001721
Simon Kelley4cb1b322012-02-06 14:30:41 +00001722 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1723 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001724 start += start[1] + 2;
1725 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001726}
1727
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001728static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001729{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001730 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001731 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001732 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001733 return NULL;
1734 else if (*p == OPTION_END)
1735 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001736 else if (*p == OPTION_PAD)
1737 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001738 else
1739 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001740 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001741 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001742 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001743 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001744 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001745 return NULL; /* malformed packet */
1746 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001747 return p;
1748 p += opt_len + 2;
1749 }
1750 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001751}
1752
Simon Kelleycdeda282006-03-16 20:16:06 +00001753static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001754{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001755 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001756
Simon Kelley3be34542004-09-11 19:12:13 +01001757 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001758 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1759 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001760
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001761 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001762 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001763 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001764
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001765 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001766 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001767 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1768 return ret;
1769
1770 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001771 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001772 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1773 return ret;
1774
1775 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001776}
1777
Simon Kelley4cb1b322012-02-06 14:30:41 +00001778static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001779{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001780 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001781 /* struct in_addr is network byte order */
1782 struct in_addr ret;
1783
Simon Kelley4cb1b322012-02-06 14:30:41 +00001784 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001785
1786 return ret;
1787}
1788
Simon Kelley7622fc02009-06-04 20:32:05 +01001789static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001790{
1791 /* this worries about unaligned data and byte order */
1792 unsigned int ret = 0;
1793 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001794 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001795
1796 for (i = 0; i < size; i++)
1797 ret = (ret << 8) | *p++;
1798
1799 return ret;
1800}
1801
1802static unsigned char *dhcp_skip_opts(unsigned char *start)
1803{
1804 while (*start != 0)
1805 start += start[1] + 2;
1806 return start;
1807}
1808
1809/* only for use when building packet: doesn't check for bad data. */
1810static unsigned char *find_overload(struct dhcp_packet *mess)
1811{
1812 unsigned char *p = &mess->options[0] + sizeof(u32);
1813
1814 while (*p != 0)
1815 {
1816 if (*p == OPTION_OVERLOAD)
1817 return p;
1818 p += p[1] + 2;
1819 }
1820 return NULL;
1821}
1822
Simon Kelley7de060b2011-08-26 17:24:52 +01001823static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1824{
1825 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1826 unsigned char *overload;
1827 size_t ret;
1828
1829 /* move agent_id back down to the end of the packet */
1830 if (agent_id)
1831 {
1832 memmove(p, agent_id, real_end - agent_id);
1833 p += real_end - agent_id;
1834 memset(p, 0, real_end - p); /* in case of overlap */
1835 }
1836
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001837 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001838 overload = find_overload(mess);
1839
1840 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001841 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001842 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001843 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001844 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001845 }
Simon Kelley28866e92011-02-14 20:19:14 +00001846 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001847 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1848
1849 if (overload && (option_uint(overload, 0, 1) & 2))
1850 {
1851 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001852 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001853 log_options(mess->sname, mess->xid);
1854 }
Simon Kelley28866e92011-02-14 20:19:14 +00001855 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001856 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1857
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001858
1859 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001860
Simon Kelley28866e92011-02-14 20:19:14 +00001861 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001862 {
1863 if (mess->siaddr.s_addr != 0)
1864 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1865
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001866 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1867 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1868
Simon Kelley7622fc02009-06-04 20:32:05 +01001869 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1870 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001871
1872 ret = (size_t)(p - (unsigned char *)mess);
1873
1874 if (ret < MIN_PACKETSZ)
1875 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001876
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001877 return ret;
1878}
1879
1880static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1881{
1882 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1883
1884 if (p + len + 3 >= end)
1885 /* not enough space in options area, try and use overload, if poss */
1886 {
1887 unsigned char *overload;
1888
1889 if (!(overload = find_overload(mess)) &&
1890 (mess->file[0] == 0 || mess->sname[0] == 0))
1891 {
1892 /* attempt to overload fname and sname areas, we've reserved space for the
1893 overflow option previuously. */
1894 overload = p;
1895 *(p++) = OPTION_OVERLOAD;
1896 *(p++) = 1;
1897 }
1898
1899 p = NULL;
1900
1901 /* using filename field ? */
1902 if (overload)
1903 {
1904 if (mess->file[0] == 0)
1905 overload[2] |= 1;
1906
1907 if (overload[2] & 1)
1908 {
1909 p = dhcp_skip_opts(mess->file);
1910 if (p + len + 3 >= mess->file + sizeof(mess->file))
1911 p = NULL;
1912 }
1913
1914 if (!p)
1915 {
1916 /* try to bring sname into play (it may be already) */
1917 if (mess->sname[0] == 0)
1918 overload[2] |= 2;
1919
1920 if (overload[2] & 2)
1921 {
1922 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001923 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001924 p = NULL;
1925 }
1926 }
1927 }
1928
1929 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001930 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 +00001931 }
1932
1933 if (p)
1934 {
1935 *(p++) = opt;
1936 *(p++) = len;
1937 }
1938
1939 return p;
1940}
1941
1942static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1943{
1944 int i;
1945 unsigned char *p = free_space(mess, end, opt, len);
1946
1947 if (p)
1948 for (i = 0; i < len; i++)
1949 *(p++) = val >> (8 * (len - (i + 1)));
1950}
1951
1952static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
Wang Shanker4ded9622020-12-04 10:17:35 +08001953 const char *string, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001954{
1955 unsigned char *p;
1956 size_t len = strlen(string);
1957
1958 if (null_term && len != 255)
1959 len++;
1960
1961 if ((p = free_space(mess, end, opt, len)))
1962 memcpy(p, string, len);
1963}
1964
1965/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001966static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001967{
1968 int len = opt->len;
1969
1970 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1971 len++;
1972
1973 if (p && len != 0)
1974 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001975 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001976 {
1977 int j;
1978 struct in_addr *a = (struct in_addr *)opt->val;
1979 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1980 {
1981 /* zero means "self" (but not in vendorclass options.) */
1982 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001983 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001984 else
1985 memcpy(p, a, INADDRSZ);
1986 p += INADDRSZ;
1987 }
1988 }
1989 else
Simon Kelley625ac282013-07-02 21:19:32 +01001990 /* empty string may be extended to "\0" by null_term */
1991 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001992 }
1993 return len;
1994}
Simon Kelley7622fc02009-06-04 20:32:05 +01001995
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001996static int in_list(unsigned char *list, int opt)
1997{
1998 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001999
2000 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01002001 if (!list)
2002 return 1;
2003
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002004 for (i = 0; list[i] != OPTION_END; i++)
2005 if (opt == list[i])
2006 return 1;
2007
2008 return 0;
2009}
2010
Simon Kelley7de060b2011-08-26 17:24:52 +01002011static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002012{
Simon Kelley7de060b2011-08-26 17:24:52 +01002013 struct dhcp_opt *opts;
2014
2015 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
2016 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
2017 return opts;
2018
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002019 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002020}
2021
Simon Kelley6b010842007-02-12 20:32:07 +00002022/* mark vendor-encapsulated options which match the client-supplied or
2023 config-supplied vendor class */
2024static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
2025{
2026 for (; dopt; dopt = dopt->next)
2027 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002028 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00002029 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00002030 {
Wang Shanker4ded9622020-12-04 10:17:35 +08002031 const struct dhcp_pxe_vendor *pv;
2032 struct dhcp_pxe_vendor dummy_vendor = {
2033 .data = (char *)dopt->u.vendor_class,
2034 .next = NULL,
2035 };
2036 if (dopt->flags & DHOPT_VENDOR_PXE)
2037 pv = daemon->dhcp_pxe_vendors;
2038 else
2039 pv = &dummy_vendor;
2040 for (; pv; pv = pv->next)
2041 {
2042 int i, len = 0, matched = 0;
2043 if (pv->data)
2044 len = strlen(pv->data);
2045 for (i = 0; i <= (option_len(opt) - len); i++)
2046 if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0)
2047 {
2048 matched = 1;
2049 break;
2050 }
2051 if (matched)
2052 {
2053 dopt->flags |= DHOPT_VENDOR_MATCH;
2054 break;
2055 }
2056 }
Simon Kelley6b010842007-02-12 20:32:07 +00002057 }
2058 }
2059}
2060
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002061static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
2062 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00002063{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002064 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01002065 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00002066 unsigned char *p;
2067
2068 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01002069 for (enc_len = 0, start = opt; opt; opt = opt->next)
2070 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002071 {
2072 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002073 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00002074 if (enc_len + new <= 255)
2075 enc_len += new;
2076 else
2077 {
2078 p = free_space(mess, end, encap, enc_len);
2079 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002080 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00002081 {
2082 len = do_opt(start, p + 2, NULL, null_term);
2083 *(p++) = start->opt;
2084 *(p++) = len;
2085 p += len;
2086 }
2087 enc_len = new;
2088 start = opt;
2089 }
2090 }
2091
2092 if (enc_len != 0 &&
2093 (p = free_space(mess, end, encap, enc_len + 1)))
2094 {
2095 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002096 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002097 {
2098 len = do_opt(start, p + 2, NULL, null_term);
2099 *(p++) = start->opt;
2100 *(p++) = len;
2101 p += len;
2102 }
2103 *p = OPTION_END;
2104 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002105
2106 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002107}
2108
Wang Shanker4ded9622020-12-04 10:17:35 +08002109static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor)
Simon Kelley91dccd02005-03-31 17:48:32 +01002110{
Simon Kelley7622fc02009-06-04 20:32:05 +01002111 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002112
Wang Shanker4ded9622020-12-04 10:17:35 +08002113 if (!pxevendor)
2114 pxevendor="PXEClient";
2115 option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002116 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2117 memcpy(p, uuid, 17);
2118}
2119
2120static int prune_vendor_opts(struct dhcp_netid *netid)
2121{
2122 int force = 0;
2123 struct dhcp_opt *opt;
2124
2125 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2126 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2127 if (opt->flags & DHOPT_VENDOR_MATCH)
2128 {
2129 if (!match_netid(opt->netid, netid, 1))
2130 opt->flags &= ~DHOPT_VENDOR_MATCH;
2131 else if (opt->flags & DHOPT_FORCE)
2132 force = 1;
2133 }
2134 return force;
2135}
2136
Simon Kelley8628cd62016-05-10 17:31:48 +01002137
2138/* Many UEFI PXE implementations have badly broken menu code.
2139 If there's exactly one relevant menu item, we abandon the menu system,
2140 and jamb the data direct into the DHCP file, siaddr and sname fields.
2141 Note that in this case, we have to assume that layer zero would be requested
2142 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002143static 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 +01002144{
2145 struct pxe_service *service, *found;
2146
2147 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002148 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002149 return 0;
2150
2151 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002152 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002153 {
2154 if (found)
2155 return 0; /* More than one relevant menu item */
2156
2157 found = service;
2158 }
2159
2160 if (!found)
2161 return 0; /* No relevant menu items. */
2162
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002163 if (!pxe)
2164 return 1;
2165
Simon Kelley8628cd62016-05-10 17:31:48 +01002166 if (found->sname)
2167 {
2168 mess->siaddr = a_record_from_hosts(found->sname, now);
2169 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2170 }
2171 else
2172 {
2173 if (found->server.s_addr != 0)
2174 mess->siaddr = found->server;
2175 else
2176 mess->siaddr = local;
2177
2178 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2179 }
2180
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002181 snprintf((char *)mess->file, sizeof(mess->file),
2182 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
2183
Simon Kelley8628cd62016-05-10 17:31:48 +01002184 return 1;
2185}
2186
Simon Kelley751d6f42012-02-10 15:24:51 +00002187static 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 +01002188{
2189#define NUM_OPTS 4
2190
2191 unsigned char *p, *q;
2192 struct pxe_service *service;
2193 static struct dhcp_opt *o, *ret;
2194 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002195 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002196
2197 /* We pass back references to these, hence they are declared static */
2198 static unsigned char discovery_control;
2199 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2200 static struct dhcp_opt *fake_opts = NULL;
2201
Simon Kelley316e2732010-01-22 20:16:09 +00002202 /* Disable multicast, since we don't support it, and broadcast
2203 unless we need it */
2204 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002205
2206 ret = daemon->dhcp_opts;
2207
2208 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2209 return ret;
2210
2211 for (i = 0; i < NUM_OPTS; i++)
2212 {
2213 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2214 fake_opts[i].netid = NULL;
2215 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2216 }
2217
2218 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2219 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002220 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002221
2222 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2223 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2224 {
2225 size_t len = strlen(service->menu);
2226 /* opt 43 max size is 255. encapsulated option has type and length
2227 bytes, so its max size is 253. */
2228 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2229 {
2230 *(p++) = service->type >> 8;
2231 *(p++) = service->type;
2232 *(p++) = len;
2233 memcpy(p, service->menu, len);
2234 p += len;
2235 i++;
2236 }
2237 else
2238 {
2239 toobig:
2240 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2241 return daemon->dhcp_opts;
2242 }
2243
Simon Kelley751d6f42012-02-10 15:24:51 +00002244 boot_server = service->basename ? local :
2245 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2246
Simon Kelley316e2732010-01-22 20:16:09 +00002247 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002248 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002249 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002250 goto toobig;
2251
2252 /* Boot service with known address - give it */
2253 *(q++) = service->type >> 8;
2254 *(q++) = service->type;
2255 *(q++) = 1;
2256 /* dest misaligned */
2257 memcpy(q, &boot_server.s_addr, INADDRSZ);
2258 q += INADDRSZ;
2259 }
2260 else if (service->type != 0)
2261 /* We don't know the server for a service type, so we'll
2262 allow the client to broadcast for it */
2263 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002264 }
2265
2266 /* if no prompt, wait forever if there's a choice */
2267 fake_prompt[0] = (i > 1) ? 255 : 0;
2268
2269 if (i == 0)
2270 discovery_control = 8; /* no menu - just use use mess->filename */
2271 else
2272 {
2273 ret = &fake_opts[j--];
2274 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2275 ret->val = (unsigned char *)daemon->dhcp_buff;
2276 ret->opt = SUBOPT_PXE_MENU;
2277
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002278 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002279 {
2280 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002281 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2282 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002283 ret->opt = SUBOPT_PXE_SERVERS;
2284 }
2285 }
2286
2287 for (o = daemon->dhcp_opts; o; o = o->next)
2288 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2289 break;
2290
2291 if (!o)
2292 {
2293 ret = &fake_opts[j--];
2294 ret->len = sizeof(fake_prompt);
2295 ret->val = fake_prompt;
2296 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2297 }
2298
Simon Kelley316e2732010-01-22 20:16:09 +00002299 ret = &fake_opts[j--];
2300 ret->len = 1;
2301 ret->opt = SUBOPT_PXE_DISCOVERY;
2302 ret->val= &discovery_control;
2303
Simon Kelley7622fc02009-06-04 20:32:05 +01002304 return ret;
2305}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002306
2307static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002308{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002309 memset(mess->sname, 0, sizeof(mess->sname));
2310 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002311 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002312 mess->siaddr.s_addr = 0;
2313}
Simon Kelleycdeda282006-03-16 20:16:06 +00002314
Simon Kelley7622fc02009-06-04 20:32:05 +01002315struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002316{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002317 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002318
2319 /* decide which dhcp-boot option we're using */
2320 for (boot = daemon->boot_config; boot; boot = boot->next)
2321 if (match_netid(boot->netid, netid, 0))
2322 break;
2323 if (!boot)
2324 /* No match, look for one without a netid */
2325 for (boot = daemon->boot_config; boot; boot = boot->next)
2326 if (match_netid(boot->netid, netid, 1))
2327 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002328
2329 return boot;
2330}
2331
Wang Shanker4ded9622020-12-04 10:17:35 +08002332static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor)
2333{
2334 const unsigned char *opt = NULL;
2335 ssize_t conf_len = 0;
2336 const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors;
2337 opt = option_find(mess, sz, OPTION_VENDOR_ID, 0);
2338 if (!opt)
2339 return 0;
2340 for (; conf; conf = conf->next)
2341 {
2342 conf_len = strlen(conf->data);
2343 if (option_len(opt) < conf_len)
2344 continue;
2345 if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0)
2346 {
2347 if (pxe_vendor)
2348 *pxe_vendor = conf->data;
2349 return 1;
2350 }
2351 }
2352 return 0;
2353}
2354
Simon Kelley7622fc02009-06-04 20:32:05 +01002355static void do_options(struct dhcp_context *context,
2356 struct dhcp_packet *mess,
2357 unsigned char *end,
2358 unsigned char *req_options,
2359 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002360 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002361 struct dhcp_netid *netid,
2362 struct in_addr subnet_addr,
2363 unsigned char fqdn_flags,
2364 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002365 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002366 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002367 time_t now,
2368 unsigned int lease_time,
Wang Shanker4ded9622020-12-04 10:17:35 +08002369 unsigned short fuzz,
2370 const char *pxevendor)
Simon Kelley7622fc02009-06-04 20:32:05 +01002371{
2372 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2373 struct dhcp_boot *boot;
2374 unsigned char *p;
2375 int i, len, force_encap = 0;
2376 unsigned char f0 = 0, s0 = 0;
2377 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002378 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002379 struct dhcp_netid *tagif;
2380 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002381
Simon Kelley4cb1b322012-02-06 14:30:41 +00002382 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002383 if (context)
2384 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002385 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002386
Simon Kelley7622fc02009-06-04 20:32:05 +01002387 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002388 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002389 {
2390 char *q = daemon->namebuff;
2391 for (i = 0; req_options[i] != OPTION_END; i++)
2392 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002393 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002394 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2395 "%d%s%s%s",
2396 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002397 strlen(s) != 0 ? ":" : "",
2398 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002399 req_options[i+1] == OPTION_END ? "" : ", ");
2400 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2401 {
2402 q = daemon->namebuff;
2403 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2404 }
2405 }
2406 }
2407
Simon Kelley7de060b2011-08-26 17:24:52 +01002408 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2409 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2410 break;
2411 if (id_list)
2412 mess->flags |= htons(0x8000); /* force broadcast */
2413
Simon Kelley73a08a22009-02-05 20:28:08 +00002414 if (context)
2415 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002416
2417 /* See if we can send the boot stuff as options.
2418 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002419 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002420 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002421 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002422 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002423 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002424 {
2425 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002426 {
Simon Kelley28866e92011-02-14 20:19:14 +00002427 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002428 req_options &&
2429 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002430 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2431 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002432 safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002433 }
2434
2435 if (boot->file)
2436 {
Simon Kelley28866e92011-02-14 20:19:14 +00002437 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002438 req_options &&
2439 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002440 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2441 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002442 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002443 }
2444
Simon Kelley7de060b2011-08-26 17:24:52 +01002445 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002446 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002447 else if (boot->tftp_sname)
2448 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002449 }
Simon Kelley824af852008-02-12 20:43:05 +00002450 else
2451 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002452 they're not explicitly asked for as options. OPTION_END is used
2453 as an internal way to specify siaddr without using dhcp-boot, for use in
2454 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002455 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002456 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002457 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002458 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002459 safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
Simon Kelley824af852008-02-12 20:43:05 +00002460 done_file = 1;
2461 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002462
Simon Kelley824af852008-02-12 20:43:05 +00002463 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002464 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002465 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002466 safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
Simon Kelley824af852008-02-12 20:43:05 +00002467 done_server = 1;
2468 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002469
Simon Kelley7de060b2011-08-26 17:24:52 +01002470 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002471 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002472 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002473
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002474 /* We don't want to do option-overload for BOOTP, so make the file and sname
2475 fields look like they are in use, even when they aren't. This gets restored
2476 at the end of this function. */
2477
Simon Kelley28866e92011-02-14 20:19:14 +00002478 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002479 {
2480 f0 = mess->file[0];
2481 mess->file[0] = 1;
2482 s0 = mess->sname[0];
2483 mess->sname[0] = 1;
2484 }
2485
2486 /* At this point, if mess->sname or mess->file are zeroed, they are available
2487 for option overload, reserve space for the overload option. */
2488 if (mess->file[0] == 0 || mess->sname[0] == 0)
2489 end -= 3;
2490
Simon Kelley3be34542004-09-11 19:12:13 +01002491 /* rfc3011 says this doesn't need to be in the requested options list. */
2492 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002493 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002494
2495 if (lease_time != 0xffffffff)
2496 {
2497 unsigned int t1val = lease_time/2;
2498 unsigned int t2val = (lease_time*7)/8;
2499 unsigned int hval;
2500
2501 /* If set by user, sanity check, so not longer than lease. */
2502 if ((opt = option_find2(OPTION_T1)))
2503 {
2504 hval = ntohl(*((unsigned int *)opt->val));
2505 if (hval < lease_time && hval > 2)
2506 t1val = hval;
2507 }
2508
2509 if ((opt = option_find2(OPTION_T2)))
2510 {
2511 hval = ntohl(*((unsigned int *)opt->val));
2512 if (hval < lease_time && hval > 2)
2513 t2val = hval;
2514 }
2515
Simon Kelley7c0f2542015-05-14 21:16:18 +01002516 /* ensure T1 is still < T2 */
2517 if (t2val <= t1val)
2518 t1val = t2val - 1;
2519
Simon Kelleyca85a282015-05-13 22:33:04 +01002520 while (fuzz > (t1val/8))
2521 fuzz = fuzz/2;
2522
2523 t1val -= fuzz;
2524 t2val -= fuzz;
2525
Simon Kelleyca85a282015-05-13 22:33:04 +01002526 option_put(mess, end, OPTION_T1, 4, t1val);
2527 option_put(mess, end, OPTION_T2, 4, t2val);
2528 }
2529
Simon Kelley73a08a22009-02-05 20:28:08 +00002530 /* replies to DHCPINFORM may not have a valid context */
2531 if (context)
2532 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002533 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002534 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2535
2536 /* May not have a "guessed" broadcast address if we got no packets via a relay
2537 from this net yet (ie just unicast renewals after a restart */
2538 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002539 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002540 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2541
2542 /* Same comments as broadcast apply, and also may not be able to get a sensible
2543 default when using subnet select. User must configure by steam in that case. */
2544 if (context->router.s_addr &&
2545 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002546 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002547 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2548
Simon Kelleya21e27b2013-02-17 16:41:35 +00002549 if (daemon->port == NAMESERVER_PORT &&
2550 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002551 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002552 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2553 }
Simon Kelley3be34542004-09-11 19:12:13 +01002554
Simon Kelley9009d742008-11-14 20:04:27 +00002555 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002556 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002557 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002558
Simon Kelley824af852008-02-12 20:43:05 +00002559 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002560 if (hostname)
2561 {
Simon Kelley824af852008-02-12 20:43:05 +00002562 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002563 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002564 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002565
2566 if (fqdn_flags != 0)
2567 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002568 len = strlen(hostname) + 3;
2569
Simon Kelley3d8df262005-08-29 12:19:27 +01002570 if (fqdn_flags & 0x04)
2571 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002572 else if (null_term)
2573 len++;
2574
Simon Kelley9009d742008-11-14 20:04:27 +00002575 if (domain)
2576 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002577 else if (fqdn_flags & 0x04)
2578 len--;
2579
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002580 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002581 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002582 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002583 *(p++) = 255;
2584 *(p++) = 255;
2585
2586 if (fqdn_flags & 0x04)
2587 {
Simon Kelley0549c732017-09-25 18:17:11 +01002588 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002589 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002590 {
Simon Kelley0549c732017-09-25 18:17:11 +01002591 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002592 *p++ = 0;
2593 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002594 }
2595 else
2596 {
2597 memcpy(p, hostname, strlen(hostname));
2598 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002599 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002600 {
2601 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002602 memcpy(p, domain, strlen(domain));
2603 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002604 }
2605 if (null_term)
2606 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002607 }
2608 }
2609 }
2610 }
2611
Simon Kelley6b010842007-02-12 20:32:07 +00002612 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002613 {
Simon Kelley824af852008-02-12 20:43:05 +00002614 int optno = opt->opt;
2615
Simon Kelley7de060b2011-08-26 17:24:52 +01002616 /* netids match and not encapsulated? */
2617 if (!(opt->flags & DHOPT_TAGOK))
2618 continue;
2619
Simon Kelley6b010842007-02-12 20:32:07 +00002620 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002621 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002622 continue;
2623
Simon Kelleyca85a282015-05-13 22:33:04 +01002624 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002625 if (optno == OPTION_CLIENT_FQDN ||
2626 optno == OPTION_MAXMESSAGE ||
2627 optno == OPTION_OVERLOAD ||
2628 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002629 optno == OPTION_END ||
2630 optno == OPTION_T1 ||
2631 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002632 continue;
2633
2634 if (optno == OPTION_SNAME && done_server)
2635 continue;
2636
2637 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002638 continue;
2639
Simon Kelley33820b72004-04-03 21:10:00 +01002640 /* For the options we have default values on
2641 dhc-option=<optionno> means "don't include this option"
2642 not "include a zero-length option" */
2643 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002644 (optno == OPTION_NETMASK ||
2645 optno == OPTION_BROADCAST ||
2646 optno == OPTION_ROUTER ||
2647 optno == OPTION_DNSSERVER ||
2648 optno == OPTION_DOMAINNAME ||
2649 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002650 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002651
2652 /* vendor-class comes from elsewhere for PXE */
2653 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2654 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002655
Simon Kelley7622fc02009-06-04 20:32:05 +01002656 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002657 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002658 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002659
Simon Kelley824af852008-02-12 20:43:05 +00002660 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002661 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002662 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002663 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2664
Simon Kelley6b010842007-02-12 20:32:07 +00002665 /* If we send a vendor-id, revisit which vendor-ops we consider
2666 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002667 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002668 {
2669 match_vendor_opts(p - 2, config_opts);
2670 done_vendor_class = 1;
2671 }
Simon Kelley6b010842007-02-12 20:32:07 +00002672 }
2673 }
2674
Simon Kelley73a08a22009-02-05 20:28:08 +00002675 /* Now send options to be encapsulated in arbitrary options,
2676 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002677 Also handle vendor-identifying vendor-encapsulated options,
2678 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002679 The may be more that one "outer" to do, so group
2680 all the options which match each outer in turn. */
2681 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002682 opt->flags &= ~DHOPT_ENCAP_DONE;
2683
2684 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002685 {
2686 int flags;
2687
2688 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2689 {
2690 int found = 0;
2691 struct dhcp_opt *o;
2692
2693 if (opt->flags & DHOPT_ENCAP_DONE)
2694 continue;
2695
2696 for (len = 0, o = config_opts; o; o = o->next)
2697 {
2698 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2699
2700 o->flags &= ~DHOPT_ENCAP_MATCH;
2701
2702 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2703 continue;
2704
2705 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002706 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002707 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2708 {
2709 o->flags |= DHOPT_ENCAP_MATCH;
2710 found = 1;
2711 len += do_opt(o, NULL, NULL, 0) + 2;
2712 }
2713 }
2714
2715 if (found)
2716 {
2717 if (flags & DHOPT_ENCAPSULATE)
2718 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2719 else if (len > 250)
2720 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2721 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2722 {
2723 int swap_ent = htonl(opt->u.encap);
2724 memcpy(p, &swap_ent, 4);
2725 p += 4;
2726 *(p++) = len;
2727 for (o = config_opts; o; o = o->next)
2728 if (o->flags & DHOPT_ENCAP_MATCH)
2729 {
2730 len = do_opt(o, p + 2, NULL, 0);
2731 *(p++) = o->opt;
2732 *(p++) = len;
2733 p += len;
2734 }
2735 }
2736 }
2737 }
2738 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002739
Simon Kelley7de060b2011-08-26 17:24:52 +01002740 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002741
Simon Kelley316e2732010-01-22 20:16:09 +00002742 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002743 {
Wang Shanker4ded9622020-12-04 10:17:35 +08002744 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002745 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002746 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002747 }
2748
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002749 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2750 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2751 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2752 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2753 /* If we send vendor encapsulated options, and haven't already sent option 60,
2754 echo back the value we got from the client. */
2755 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2756
Simon Kelley7622fc02009-06-04 20:32:05 +01002757 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002758 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002759 {
2760 mess->file[0] = f0;
2761 mess->sname[0] = s0;
2762 }
2763}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002764
Floris Bos503c6092017-04-09 23:07:13 +01002765static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2766{
2767 struct delay_config *delay_conf;
2768
2769 /* Decide which delay_config option we're using */
2770 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2771 if (match_netid(delay_conf->netid, netid, 0))
2772 break;
2773
2774 if (!delay_conf)
2775 /* No match, look for one without a netid */
2776 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2777 if (match_netid(delay_conf->netid, netid, 1))
2778 break;
2779
2780 if (delay_conf)
2781 {
2782 if (!option_bool(OPT_QUIET_DHCP))
2783 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2784 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2785 }
2786}
2787
Simon Kelley7622fc02009-06-04 20:32:05 +01002788#endif
2789
2790
2791
2792
2793
2794
2795