blob: 68834ea503ff7610c10bd27673e1bf9773ca463c [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 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 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700375 const char *via;
376 if (subnet_addr.s_addr)
377 {
378 via = _("with subnet selector");
379 inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
380 }
381 else
382 {
383 via = _("via");
384 if (mess->giaddr.s_addr)
385 inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
386 else
387 safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
388 }
389 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
390 via, daemon->addrbuff);
Simon Kelley3be34542004-09-11 19:12:13 +0100391 return 0;
392 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100393
Simon Kelley28866e92011-02-14 20:19:14 +0000394 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100395 {
396 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100397 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
398 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700399 inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
Simon Kelley7622fc02009-06-04 20:32:05 +0100400 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700401 {
402 inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
403 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
404 ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
405 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100406 else
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700407 {
408 inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
409 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
410 ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
411 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100412 }
413 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100414
415 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
416 Otherwise assume the option is an array, and look for a matching element.
Josh Soref730c6742017-02-06 16:14:04 +0000417 If no data given, existence of the option is enough. This code handles
Simon Kelley86e92f92013-04-23 11:31:39 +0100418 rfc3925 V-I classes too. */
419 for (o = daemon->dhcp_match; o; o = o->next)
420 {
421 unsigned int len, elen, match = 0;
422 size_t offset, o2;
423
424 if (o->flags & DHOPT_RFC3925)
425 {
426 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
427 continue;
428
429 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
430 {
431 len = option_uint(opt, offset + 4 , 1);
432 /* Need to take care that bad data can't run us off the end of the packet */
Matthias Andree9828ab12017-05-21 22:41:16 +0100433 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100434 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
435 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
436 {
437 elen = option_uint(opt, o2, 1);
Simon Kelleyfc193992020-03-05 22:13:45 +0000438 if ((o2 + elen + 1 <= (unsigned)option_len(opt)) &&
Simon Kelley86e92f92013-04-23 11:31:39 +0100439 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
440 break;
441 }
442 if (match)
443 break;
444 }
445 }
446 else
447 {
448 if (!(opt = option_find(mess, sz, o->opt, 1)))
449 continue;
450
451 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
452 }
453
454 if (match)
455 {
456 o->netid->next = netid;
457 netid = o->netid;
458 }
459 }
460
461 /* user-class options are, according to RFC3004, supposed to contain
462 a set of counted strings. Here we check that this is so (by seeing
463 if the counts are consistent with the overall option length) and if
464 so zero the counts so that we don't get spurious matches between
465 the vendor string and the counts. If the lengths don't add up, we
466 assume that the option is a single string and non RFC3004 compliant
467 and just do the substring match. dhclient provides these broken options.
468 The code, later, which sends user-class data to the lease-change script
469 relies on the transformation done here.
470 */
471
472 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
473 {
474 unsigned char *ucp = option_ptr(opt, 0);
475 int tmp, j;
476 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
477 if (j == option_len(opt))
478 for (j = 0; j < option_len(opt); j = tmp)
479 {
480 tmp = j + ucp[j] + 1;
481 ucp[j] = 0;
482 }
483 }
484
485 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
486 {
487 int mopt;
488
489 if (vendor->match_type == MATCH_VENDOR)
490 mopt = OPTION_VENDOR_ID;
491 else if (vendor->match_type == MATCH_USER)
492 mopt = OPTION_USER_CLASS;
493 else
494 continue;
495
496 if ((opt = option_find(mess, sz, mopt, 1)))
497 {
498 int i;
499 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
500 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
501 {
502 vendor->netid.next = netid;
503 netid = &vendor->netid;
504 break;
505 }
506 }
507 }
508
509 /* mark vendor-encapsulated options which match the client-supplied vendor class,
510 save client-supplied vendor class */
511 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
512 {
513 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
514 vendor_class_len = option_len(opt);
515 }
516 match_vendor_opts(opt, daemon->dhcp_opts);
517
518 if (option_bool(OPT_LOG_OPTS))
519 {
520 if (sanitise(opt, daemon->namebuff))
521 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
522 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
523 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
524 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100525
Simon Kelley3be34542004-09-11 19:12:13 +0100526 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100527
Simon Kelleycdeda282006-03-16 20:16:06 +0000528 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
Simon Kelley52ec7832020-02-07 21:05:54 +0000529 mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid));
Simon Kelley5aabfc72007-08-29 11:24:47 +0100530
531 /* set "known" tag for known hosts */
532 if (config)
533 {
534 known_id.net = "known";
535 known_id.next = netid;
536 netid = &known_id;
537 }
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100538 else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
Simon Kelley52ec7832020-02-07 21:05:54 +0000539 mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)))
Simon Kelleyb2a9c572017-04-30 18:21:31 +0100540 {
541 known_id.net = "known-othernet";
542 known_id.next = netid;
543 netid = &known_id;
544 }
Simon Kelley26128d22004-11-14 16:43:54 +0000545
Simon Kelley316e2732010-01-22 20:16:09 +0000546 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100547 {
548 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000549 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000550 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100551
552 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100553 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100554 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000555
Simon Kelley26128d22004-11-14 16:43:54 +0000556 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000557 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000558
559 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
560
561 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000562 {
563 hostname = config->hostname;
564 domain = config->domain;
565 }
566
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100567 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000568 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100569 struct dhcp_netid_list *list;
570
571 for (list = config->netid; list; list = list->next)
572 {
573 list->list->next = netid;
574 netid = list->list;
575 }
Simon Kelley26128d22004-11-14 16:43:54 +0000576 }
577
578 /* Match incoming filename field as a netid. */
579 if (mess->file[0])
580 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000581 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
582 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
583 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000584 id.next = netid;
585 netid = &id;
586 }
Simon Kelley6b010842007-02-12 20:32:07 +0000587
588 /* Add "bootp" as a tag to allow different options, address ranges etc
589 for BOOTP clients */
590 bootp_id.net = "bootp";
591 bootp_id.next = netid;
592 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000593
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100594 tagif_netid = run_tag_if(netid);
595
Simon Kelley26128d22004-11-14 16:43:54 +0000596 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100597 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100598 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000599
Simon Kelley3d8df262005-08-29 12:19:27 +0100600 if (!message)
601 {
Simon Kelley9009d742008-11-14 20:04:27 +0000602 int nailed = 0;
603
Simon Kelley3d8df262005-08-29 12:19:27 +0100604 if (have_config(config, CONFIG_ADDR))
605 {
Simon Kelley9009d742008-11-14 20:04:27 +0000606 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100607 logaddr = &config->addr;
608 mess->yiaddr = config->addr;
609 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000610 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100611 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000612 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000613 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100614 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100615 else
616 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000617 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100618 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000619 {
620 if (lease)
621 {
622 /* lease exists, wrong network. */
623 lease_prune(lease, now);
624 lease = NULL;
625 }
Simon Kelleyc7be0162017-05-10 22:21:53 +0100626 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
Simon Kelleye17fb622006-01-14 20:33:46 +0000627 message = _("no address available");
628 }
629 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100630 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100631 }
632
Simon Kelley9009d742008-11-14 20:04:27 +0000633 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
634 message = _("wrong network");
635 else if (context->netid.net)
636 {
637 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100638 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100639 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100640
Simon Kelley4cb1b322012-02-06 14:30:41 +0000641 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100642
Simon Kelley9009d742008-11-14 20:04:27 +0000643 if (!message && !nailed)
644 {
645 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100646 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000647 break;
648 if (!id_list)
649 message = _("no address configured");
650 }
651
Simon Kelley7cebd202006-05-06 14:13:33 +0100652 if (!message &&
653 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000654 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000655 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000656
Simon Kelley3d8df262005-08-29 12:19:27 +0100657 if (!message)
658 {
659 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100660
Simon Kelleya9ab7322012-04-28 11:29:37 +0100661 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100662 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000663 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100664 /* infinite lease unless nailed in dhcp-host line. */
665 lease_set_expires(lease,
666 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
667 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000668 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100669
Simon Kelleya9df0e32017-04-28 22:43:00 +0100670 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000671 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +0800672 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100673 }
674 }
675
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +0100676 daemon->metrics[METRIC_BOOTP]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100677 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000678
Simon Kelley7de060b2011-08-26 17:24:52 +0100679 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100680 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000681
Roy Marples3f3adae2013-07-25 16:22:46 +0100682 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100683 {
684 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
685 int len = option_len(opt);
686 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100687 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100688
Simon Kelley9fed0f72012-08-30 11:43:35 +0100689 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100690 len -= 3;
691 op += 3;
692 pp = op;
693
Simon Kelley9fed0f72012-08-30 11:43:35 +0100694 /* NB, the following always sets at least one bit */
695 if (option_bool(OPT_FQDN_UPDATE))
696 {
697 if (fqdn_flags & 0x01)
698 {
699 fqdn_flags |= 0x02; /* set O */
700 fqdn_flags &= ~0x01; /* clear S */
701 }
702 fqdn_flags |= 0x08; /* set N */
703 }
704 else
705 {
706 if (!(fqdn_flags & 0x01))
707 fqdn_flags |= 0x03; /* set S and O */
708 fqdn_flags &= ~0x08; /* clear N */
709 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100710
711 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100712 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100713 {
714 memcpy(pq, op+1, *op);
715 pq += *op;
716 op += (*op)+1;
717 *(pq++) = '.';
718 }
719 else
720 {
721 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000722 if (len > 0 && op[len-1] == 0)
723 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100724 pq += len + 1;
725 }
726
727 if (pq != daemon->dhcp_buff)
728 pq--;
729
730 *pq = 0;
731
Simon Kelley1f15b812009-10-13 17:49:32 +0100732 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100733 offer_hostname = client_hostname = daemon->dhcp_buff;
734 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000735 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000736 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000737 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100738 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000739 /* Microsoft clients are broken, and need zero-terminated strings
740 in options. We detect this state here, and do the same in
741 any options we send */
742 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
743 borken_opt = 1;
744 else
745 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100746 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100747 client_hostname = daemon->dhcp_buff;
748 }
749
Simon Kelley34d41472019-12-05 23:44:29 +0000750 if (client_hostname)
751 {
752 struct dhcp_match_name *m;
753 size_t nl = strlen(client_hostname);
754
755 if (option_bool(OPT_LOG_OPTS))
756 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
757 for (m = daemon->dhcp_name_match; m; m = m->next)
758 {
759 size_t ml = strlen(m->name);
760 char save = 0;
761
762 if (nl < ml)
763 continue;
764 if (nl > ml)
765 {
766 save = client_hostname[ml];
767 client_hostname[ml] = 0;
768 }
769
770 if (hostname_isequal(client_hostname, m->name) &&
771 (save == 0 || m->wildcard))
772 {
773 m->netid->next = netid;
774 netid = m->netid;
775 }
776
777 if (save != 0)
778 client_hostname[ml] = save;
779 }
780 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100781
Simon Kelley3d8df262005-08-29 12:19:27 +0100782 if (have_config(config, CONFIG_NAME))
783 {
784 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000785 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000786 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000787 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100788 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000789 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100790 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100791 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100792 {
Simon Kelley9009d742008-11-14 20:04:27 +0000793 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100794
Simon Kelley6ebdc952019-10-30 21:04:27 +0000795 if (strlen(client_hostname) != 0)
Simon Kelley5aabfc72007-08-29 11:24:47 +0100796 {
797 hostname = client_hostname;
Simon Kelley0fdf3c12018-10-05 23:35:54 +0100798
Simon Kelley5aabfc72007-08-29 11:24:47 +0100799 if (!config)
800 {
801 /* Search again now we have a hostname.
802 Only accept configs without CLID and HWADDR here, (they won't match)
803 to avoid impersonation by name. */
804 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
805 mess->chaddr, mess->hlen,
Simon Kelley52ec7832020-02-07 21:05:54 +0000806 mess->htype, hostname, run_tag_if(netid));
Simon Kelley9009d742008-11-14 20:04:27 +0000807 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000808 {
809 config = new;
810 /* set "known" tag for known hosts */
811 known_id.net = "known";
812 known_id.next = netid;
813 netid = &known_id;
814 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100815 }
Simon Kelley6ebdc952019-10-30 21:04:27 +0000816 }
817 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100818
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100819 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100820 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100821 struct dhcp_netid_list *list;
822
823 for (list = config->netid; list; list = list->next)
824 {
825 list->list->next = netid;
826 netid = list->list;
827 }
Simon Kelleya2226412004-05-13 20:27:08 +0100828 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100829
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100830 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100831
Simon Kelley26128d22004-11-14 16:43:54 +0000832 /* if all the netids in the ignore list are present, ignore this client */
833 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100834 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000835 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100836
837 /* If configured, we can override the server-id to be the address of the relay,
838 so that all traffic goes via the relay and can pick up agent-id info. This can be
839 configured for all relays, or by address. */
840 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
841 {
842 if (!daemon->override_relays)
843 override = mess->giaddr;
844 else
845 {
846 struct addr_list *l;
847 for (l = daemon->override_relays; l; l = l->next)
848 if (l->addr.s_addr == mess->giaddr.s_addr)
849 break;
850 if (l)
851 override = mess->giaddr;
852 }
853 }
854
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100855 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
856 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000857 clid = NULL;
858
Simon Kelley7622fc02009-06-04 20:32:05 +0100859 /* Check if client is PXE client. */
Wang Shanker4ded9622020-12-04 10:17:35 +0800860 if (daemon->enable_pxe &&
861 is_pxe_client(mess, sz, &pxevendor))
Simon Kelley7622fc02009-06-04 20:32:05 +0100862 {
863 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
864 {
865 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
866 uuid = pxe_uuid;
867 }
868
869 /* Check if this is really a PXE bootserver request, and handle specially if so. */
870 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
871 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
872 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
873 {
874 struct pxe_service *service;
875 int type = option_uint(opt, 0, 2);
876 int layer = option_uint(opt, 2, 2);
877 unsigned char save71[4];
878 struct dhcp_opt opt71;
879
Simon Kelley1f15b812009-10-13 17:49:32 +0100880 if (ignore)
881 return 0;
882
Simon Kelley7622fc02009-06-04 20:32:05 +0100883 if (layer & 0x8000)
884 {
885 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
886 return 0;
887 }
888
889 memcpy(save71, option_ptr(opt, 0), 4);
890
891 for (service = daemon->pxe_services; service; service = service->next)
892 if (service->type == type)
893 break;
894
Simon Kelley549b1a42015-05-20 20:20:24 +0100895 for (; context; context = context->current)
896 if (match_netid(context->filter, tagif_netid, 1) &&
897 is_same_net(mess->ciaddr, context->start, context->netmask))
898 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100899
Simon Kelley549b1a42015-05-20 20:20:24 +0100900 if (!service || !service->basename || !context)
901 return 0;
902
Simon Kelleya9df0e32017-04-28 22:43:00 +0100903 clear_packet(mess, end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100904
905 mess->yiaddr = mess->ciaddr;
906 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000907 if (service->sname)
908 mess->siaddr = a_record_from_hosts(service->sname, now);
909 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100910 mess->siaddr = service->server;
911 else
912 mess->siaddr = context->local;
913
Matthias Andreef77700a2017-05-21 22:36:09 +0100914 if (strchr(service->basename, '.'))
915 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100916 "%s", service->basename);
Matthias Andreef77700a2017-05-21 22:36:09 +0100917 else
918 snprintf((char *)mess->file, sizeof(mess->file),
Chris Novakovic24465142017-06-06 23:02:59 +0100919 "%s.%d", service->basename, layer);
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100920
Simon Kelley7622fc02009-06-04 20:32:05 +0100921 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
922 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
Wang Shanker4ded9622020-12-04 10:17:35 +0800923 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelley7622fc02009-06-04 20:32:05 +0100924
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100925 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100926 opt71.val = save71;
927 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
928 opt71.len = 4;
929 opt71.flags = DHOPT_VENDOR_MATCH;
930 opt71.netid = NULL;
931 opt71.next = daemon->dhcp_opts;
932 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
933
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100934 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000935 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100936 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100937 }
938
939 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
940 {
941 pxearch = option_uint(opt, 0, 2);
942
Simon Kelley316e2732010-01-22 20:16:09 +0000943 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000944 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100945 {
Simon Kelley28866e92011-02-14 20:19:14 +0000946 struct dhcp_context *tmp;
Simon Kelley8628cd62016-05-10 17:31:48 +0100947 int workaround = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +0100948
Simon Kelley28866e92011-02-14 20:19:14 +0000949 for (tmp = context; tmp; tmp = tmp->current)
950 if ((tmp->flags & CONTEXT_PROXY) &&
951 match_netid(tmp->filter, tagif_netid, 1))
952 break;
953
954 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100955 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100956 struct dhcp_boot *boot;
Simon Kelley0a4a0492016-05-15 20:13:45 +0100957 int redirect4011 = 0;
958
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100959 if (tmp->netid.net)
960 {
961 tmp->netid.next = netid;
962 tagif_netid = run_tag_if(&tmp->netid);
963 }
964
965 boot = find_boot(tagif_netid);
966
Simon Kelley28866e92011-02-14 20:19:14 +0000967 mess->yiaddr.s_addr = 0;
968 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
969 {
970 mess->ciaddr.s_addr = 0;
971 mess->flags |= htons(0x8000); /* broadcast */
972 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100973
Simon Kelleya9df0e32017-04-28 22:43:00 +0100974 clear_packet(mess, end);
Simon Kelley28866e92011-02-14 20:19:14 +0000975
Simon Kelley0a4a0492016-05-15 20:13:45 +0100976 /* Redirect EFI clients to port 4011 */
977 if (pxearch >= 6)
978 {
979 redirect4011 = 1;
980 mess->siaddr = tmp->local;
981 }
982
Simon Kelleyfe71bba2016-05-14 20:50:45 +0100983 /* Returns true if only one matching service is available. On port 4011,
984 it also inserts the boot file and server name. */
985 workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
Simon Kelley8628cd62016-05-10 17:31:48 +0100986
987 if (!workaround && boot)
Simon Kelley28866e92011-02-14 20:19:14 +0000988 {
Geert Stappersc7e6aea2018-01-13 17:56:37 +0000989 /* Provide the bootfile here, for iPXE, and in case we have no menu items
Simon Kelley8628cd62016-05-10 17:31:48 +0100990 and set discovery_control = 8 */
Simon Kelley7de060b2011-08-26 17:24:52 +0100991 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000992 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100993 else if (boot->tftp_sname)
994 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000995
996 if (boot->file)
Petr Menšík47b45b22018-08-15 18:17:00 +0200997 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley28866e92011-02-14 20:19:14 +0000998 }
999
1000 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
1001 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +01001002 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Wang Shanker4ded9622020-12-04 10:17:35 +08001003 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelley28866e92011-02-14 20:19:14 +00001004 prune_vendor_opts(tagif_netid);
Simon Kelley0a4a0492016-05-15 20:13:45 +01001005 if ((pxe && !workaround) || !redirect4011)
Simon Kelley8628cd62016-05-10 17:31:48 +01001006 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
1007
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001008 daemon->metrics[METRIC_PXE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001009 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +00001010 log_tags(tagif_netid, ntohl(mess->xid));
Floris Bos503c6092017-04-09 23:07:13 +01001011 if (!ignore)
1012 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001013 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +01001014 }
Simon Kelley7622fc02009-06-04 20:32:05 +01001015 }
1016 }
1017 }
1018
1019 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +00001020 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +01001021 return 0;
1022
Simon Kelleybb01cb92004-12-13 20:56:23 +00001023 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001024 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001025 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001026 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +00001027 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001028 }
1029
Simon Kelley3be34542004-09-11 19:12:13 +01001030 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001031 {
Simon Kelley44a2a312004-03-10 20:04:35 +00001032 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +00001033 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001034 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001035 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001036
Simon Kelley44a2a312004-03-10 20:04:35 +00001037 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +01001038 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +00001039
Simon Kelleybb01cb92004-12-13 20:56:23 +00001040 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +00001041 return 0;
1042
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001043 daemon->metrics[METRIC_DHCPDECLINE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001044 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 +00001045
1046 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
1047 lease_prune(lease, now);
1048
Simon Kelley33820b72004-04-03 21:10:00 +01001049 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +00001050 config->addr.s_addr == option_addr(opt).s_addr)
1051 {
Simon Kelley849a8352006-06-09 21:02:31 +01001052 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001053 inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley7622fc02009-06-04 20:32:05 +01001054 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001055 daemon->addrbuff, daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +01001056 config->flags |= CONFIG_DECLINED;
1057 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +00001058 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001059 else
1060 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +01001061 for (; context; context = context->current)
1062 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +00001063
1064 return 0;
1065
1066 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001067 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +01001068 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +00001069 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +00001070 return 0;
1071
Simon Kelley44a2a312004-03-10 20:04:35 +00001072 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
1073 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001074 else
Simon Kelleyb8187c82005-11-26 21:46:27 +00001075 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001076
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001077 daemon->metrics[METRIC_DHCPRELEASE]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001078 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001079
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001080 return 0;
1081
1082 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001083 if (ignore || have_config(config, CONFIG_DISABLE))
1084 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +00001085 if (option_bool(OPT_QUIET_DHCP))
1086 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001087 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001088 opt = NULL;
1089 }
1090 else
1091 {
1092 struct in_addr addr, conf;
1093
Simon Kelley1a6bca82008-07-11 11:11:42 +01001094 addr.s_addr = conf.s_addr = 0;
1095
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001096 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
1097 addr = option_addr(opt);
1098
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001099 if (have_config(config, CONFIG_ADDR))
1100 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001101 inet_ntop(AF_INET, &config->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley849a8352006-06-09 21:02:31 +01001102
Simon Kelley9009d742008-11-14 20:04:27 +00001103 if ((ltmp = lease_find_by_addr(config->addr)) &&
1104 ltmp != lease &&
1105 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001106 {
1107 int len;
1108 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
1109 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +01001110 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001111 daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001112 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001113 else
1114 {
1115 struct dhcp_context *tmp;
1116 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001117 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001118 break;
1119 if (tmp)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001120 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), daemon->addrbuff);
Simon Kelley849a8352006-06-09 21:02:31 +01001121 else if (have_config(config, CONFIG_DECLINED) &&
1122 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001123 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), daemon->addrbuff);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001124 else
1125 conf = config->addr;
1126 }
1127 }
1128
1129 if (conf.s_addr)
1130 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001131 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001132 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001133 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001134 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001135 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelleyc7be0162017-05-10 22:21:53 +01001136 !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001137 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001138 else if (emac_len == 0)
1139 message = _("no unique-id");
Simon Kelleyc7be0162017-05-10 22:21:53 +01001140 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001141 message = _("no address available");
1142 }
1143
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001144 daemon->metrics[METRIC_DHCPDISCOVER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001145 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 +01001146
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001147 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001148 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001149
Simon Kelleycdeda282006-03-16 20:16:06 +00001150 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001151 {
1152 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001153 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001154 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001155
Floris Bos503c6092017-04-09 23:07:13 +01001156 apply_delay(mess->xid, recvtime, tagif_netid);
Simon Kelley734d5312018-03-23 23:09:53 +00001157
1158 if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
1159 {
1160 rapid_commit = 1;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001161 /* If a lease exists for this host and another address, squash it. */
1162 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1163 {
1164 lease_prune(lease, now);
1165 lease = NULL;
1166 }
Simon Kelley734d5312018-03-23 23:09:53 +00001167 goto rapid_commit;
1168 }
1169
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001170 log_tags(tagif_netid, ntohl(mess->xid));
1171
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001172 daemon->metrics[METRIC_DHCPOFFER]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001173 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001174
Simon Kelley824af852008-02-12 20:43:05 +00001175 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9df0e32017-04-28 22:43:00 +01001176 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001177 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001178 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001179 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001180 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001181 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001182 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001183
Simon Kelley7de060b2011-08-26 17:24:52 +01001184 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley734d5312018-03-23 23:09:53 +00001185
1186
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001187 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001188 if (ignore || have_config(config, CONFIG_DISABLE))
1189 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001190 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001191 {
1192 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001193 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001194
Simon Kelley4011c4e2006-10-28 16:26:19 +01001195 /* send vendor and user class info for new or recreated lease */
1196 do_classes = 1;
1197
Simon Kelleybb01cb92004-12-13 20:56:23 +00001198 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001199 {
Simon Kelley3be34542004-09-11 19:12:13 +01001200 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001201 selecting = 1;
1202
Simon Kelley1a6bca82008-07-11 11:11:42 +01001203 if (override.s_addr != 0)
1204 {
1205 if (option_addr(opt).s_addr != override.s_addr)
1206 return 0;
1207 }
Simon Kelley9009d742008-11-14 20:04:27 +00001208 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001209 {
1210 for (; context; context = context->current)
1211 if (context->local.s_addr == option_addr(opt).s_addr)
1212 break;
1213
1214 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001215 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001216 /* Handle very strange configs where clients have more than one route to the server.
1217 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1218 Have to set override to make sure we echo back the correct server-id */
1219 struct irec *intr;
1220
Simon Kelley115ac3e2013-05-20 11:28:32 +01001221 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001222
1223 for (intr = daemon->interfaces; intr; intr = intr->next)
1224 if (intr->addr.sa.sa_family == AF_INET &&
1225 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1226 intr->tftp_ok)
1227 break;
1228
1229 if (intr)
1230 override = intr->addr.in.sin_addr;
1231 else
1232 {
1233 /* In auth mode, a REQUEST sent to the wrong server
1234 should be faulted, so that the client establishes
1235 communication with us, otherwise, silently ignore. */
1236 if (!option_bool(OPT_AUTHORITATIVE))
1237 return 0;
1238 message = _("wrong server-ID");
1239 }
Simon Kelley9009d742008-11-14 20:04:27 +00001240 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001241 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001242
Simon Kelley3be34542004-09-11 19:12:13 +01001243 /* If a lease exists for this host and another address, squash it. */
1244 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1245 {
1246 lease_prune(lease, now);
1247 lease = NULL;
1248 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001249 }
Simon Kelley3be34542004-09-11 19:12:13 +01001250 else
1251 {
1252 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001253 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001254 return 0;
1255
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001256 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001257 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001258 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001259 }
1260 else
1261 {
1262 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001263 /* Check existing lease for this address.
1264 We allow it to be missing if dhcp-authoritative mode
1265 as long as we can allocate the lease now - checked below.
1266 This makes for a smooth recovery from a lost lease DB */
1267 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001268 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001269 {
Simon Kelley28866e92011-02-14 20:19:14 +00001270 /* A client rebinding will broadcast the request, so we may see it even
1271 if the lease is held by another server. Just ignore it in that case.
1272 If the request is unicast to us, then somethings wrong, NAK */
1273 if (!unicast_dest)
1274 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001275 message = _("lease not found");
1276 /* ensure we broadcast NAK */
1277 unicast_dest = 0;
1278 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001279
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001280 /* desynchronise renewals */
1281 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001282 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001283 }
Simon Kelley734d5312018-03-23 23:09:53 +00001284
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001285 daemon->metrics[METRIC_DHCPREQUEST]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001286 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001287
1288 rapid_commit:
Simon Kelleydfa666f2004-08-02 18:27:27 +01001289 if (!message)
1290 {
1291 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001292 struct dhcp_context *tmp = NULL;
1293
1294 if (have_config(config, CONFIG_ADDR))
1295 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001296 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001297 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001298
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001299 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001300 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001301 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001302 message = _("wrong network");
1303 /* ensure we broadcast NAK */
1304 unicast_dest = 0;
1305 }
1306
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001307 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001308 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001309 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001310 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001311
Simon Kelleydfa666f2004-08-02 18:27:27 +01001312 /* Check if a new static address has been configured. Be very sure that
1313 when the client does DISCOVER, it will get the static address, otherwise
1314 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001315 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001316 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001317 (!have_config(config, CONFIG_DECLINED) ||
1318 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001319 config->addr.s_addr != mess->yiaddr.s_addr &&
1320 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001321 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001322
1323 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001324 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001325 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001326
Simon Kelley9009d742008-11-14 20:04:27 +00001327 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1328 {
1329 /* If a host is configured with more than one MAC address, it's OK to 'nix
1330 a lease from one of it's MACs to give the address to another. */
1331 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1332 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001333 inet_ntop(AF_INET, &ltmp->addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley7622fc02009-06-04 20:32:05 +01001334 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001335 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001336 daemon->addrbuff);
Simon Kelley9009d742008-11-14 20:04:27 +00001337 lease = ltmp;
1338 }
Simon Kelley16972692006-10-16 20:04:18 +01001339 else
Simon Kelley9009d742008-11-14 20:04:27 +00001340 message = _("address in use");
1341 }
1342
1343 if (!message)
1344 {
1345 if (emac_len == 0)
1346 message = _("no unique-id");
1347
1348 else if (!lease)
1349 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001350 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001351 do_classes = 1;
1352 else
1353 message = _("no leases left");
1354 }
Simon Kelley16972692006-10-16 20:04:18 +01001355 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001356 }
Simon Kelley16972692006-10-16 20:04:18 +01001357
Simon Kelley44a2a312004-03-10 20:04:35 +00001358 if (message)
1359 {
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001360 daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
Simon Kelley734d5312018-03-23 23:09:53 +00001361 log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
1362
1363 /* rapid commit case: lease allocate failed but don't send DHCPNAK */
1364 if (rapid_commit)
1365 return 0;
Simon Kelley44a2a312004-03-10 20:04:35 +00001366
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001367 mess->yiaddr.s_addr = 0;
Simon Kelleya9df0e32017-04-28 22:43:00 +01001368 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001369 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001370 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001371 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001372 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1373 a distant subnet which unicast a REQ to us won't work. */
1374 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1375 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1376 {
1377 mess->flags |= htons(0x8000); /* broadcast */
1378 mess->ciaddr.s_addr = 0;
1379 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001380 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001381 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001382 {
Simon Kelley316e2732010-01-22 20:16:09 +00001383 if (context->netid.net)
1384 {
1385 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001386 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001387 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001388
Simon Kelley4cb1b322012-02-06 14:30:41 +00001389 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001390
Simon Kelleydcffad22012-04-24 15:25:18 +01001391 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001392 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001393 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001394 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001395
Simon Kelleydcffad22012-04-24 15:25:18 +01001396#ifdef HAVE_SCRIPT
1397 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001398 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001399 struct dhcp_netid *n;
1400
1401 if (mess->giaddr.s_addr)
1402 lease->giaddr = mess->giaddr;
1403
1404 free(lease->extradata);
1405 lease->extradata = NULL;
1406 lease->extradata_size = lease->extradata_len = 0;
1407
1408 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1409 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1410 add_extradata_opt(lease, oui);
1411 add_extradata_opt(lease, serial);
1412 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001413
1414 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1415 {
1416 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1417 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1418 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1419 }
1420 else
1421 {
1422 add_extradata_opt(lease, NULL);
1423 add_extradata_opt(lease, NULL);
1424 add_extradata_opt(lease, NULL);
1425 }
1426
ZHAO Yuf89cae32016-12-22 22:32:31 +00001427 /* DNSMASQ_REQUESTED_OPTIONS */
1428 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
1429 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001430 int i, len = option_len(opt);
ZHAO Yuf89cae32016-12-22 22:32:31 +00001431 unsigned char *rop = option_ptr(opt, 0);
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001432
ZHAO Yuf89cae32016-12-22 22:32:31 +00001433 for (i = 0; i < len; i++)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001434 lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
1435 sprintf(daemon->namebuff, "%u", rop[i]), (i + 1) == len ? 0 : ',');
ZHAO Yuf89cae32016-12-22 22:32:31 +00001436 }
1437 else
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001438 lease_add_extradata(lease, NULL, 0, 0);
1439
1440 add_extradata_opt(lease, option_find(mess, sz, OPTION_MUD_URL_V4, 1));
1441
Simon Kelleydcffad22012-04-24 15:25:18 +01001442 /* space-concat tag set */
1443 if (!tagif_netid)
1444 add_extradata_opt(lease, NULL);
1445 else
1446 for (n = tagif_netid; n; n = n->next)
1447 {
1448 struct dhcp_netid *n1;
1449 /* kill dupes */
1450 for (n1 = n->next; n1; n1 = n1->next)
1451 if (strcmp(n->net, n1->net) == 0)
1452 break;
1453 if (!n1)
1454 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1455 }
1456
1457 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1458 {
1459 int len = option_len(opt);
1460 unsigned char *ucp = option_ptr(opt, 0);
1461 /* If the user-class option started as counted strings, the first byte will be zero. */
1462 if (len != 0 && ucp[0] == 0)
1463 ucp++, len--;
Simon Kelleya93bd4b2016-03-01 18:58:01 +00001464 lease_add_extradata(lease, ucp, len, -1);
Simon Kelleydcffad22012-04-24 15:25:18 +01001465 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001466 }
Simon Kelley316e2732010-01-22 20:16:09 +00001467#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001468 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001469
1470 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1471 {
1472 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001473 hostname = client_hostname;
1474 hostname_auth = 1;
1475 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001476
Simon Kelley824af852008-02-12 20:43:05 +00001477 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001478 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001479
Simon Kelley832af0b2007-01-21 20:01:28 +00001480 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1481 if (!hostname_auth)
1482 {
1483 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001484 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001485 break;
1486 if (id_list)
1487 hostname = NULL;
1488 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001489
1490 /* Last ditch, if configured, generate hostname from mac address */
1491 if (!hostname && emac_len != 0)
1492 {
1493 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1494 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1495 break;
1496 if (id_list)
1497 {
1498 int i;
1499
1500 hostname = daemon->dhcp_buff;
1501 /* buffer is 256 bytes, 3 bytes per octet */
1502 for (i = 0; (i < emac_len) && (i < 80); i++)
1503 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1504 hostname = daemon->dhcp_buff;
1505 }
1506 }
1507
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001508 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001509 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001510
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001511 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001512 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001513
1514 if (override.s_addr != 0)
1515 lease->override = override;
1516 else
1517 override = lease->override;
1518
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001519 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001520 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley734d5312018-03-23 23:09:53 +00001521
Simon Kelleya9df0e32017-04-28 22:43:00 +01001522 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001523 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001524 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001525 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley734d5312018-03-23 23:09:53 +00001526 if (rapid_commit)
1527 option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
1528 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001529 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
Simon Kelley44a2a312004-03-10 20:04:35 +00001530 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001531
Simon Kelley7de060b2011-08-26 17:24:52 +01001532 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001533
1534 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001535 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001536 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001537
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001538 daemon->metrics[METRIC_DHCPINFORM]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001539 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001540
Simon Kelley73a08a22009-02-05 20:28:08 +00001541 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001542 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001543
1544 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001545 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001546
Simon Kelley5aabfc72007-08-29 11:24:47 +01001547 /* Find a least based on IP address if we didn't
1548 get one from MAC address/client-d */
1549 if (!lease &&
1550 (lease = lease_find_by_addr(mess->ciaddr)) &&
1551 lease->hostname)
1552 hostname = lease->hostname;
1553
Simon Kelley0f371f92013-07-27 15:15:38 +01001554 if (!hostname)
1555 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001556
Simon Kelley73a08a22009-02-05 20:28:08 +00001557 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001558 {
1559 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001560 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001561 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001562
Simon Kelley4cb1b322012-02-06 14:30:41 +00001563 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001564
Julian Kornbergeraba8bbb2018-07-21 21:55:08 +01001565 daemon->metrics[METRIC_DHCPACK]++;
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001566 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001567
Simon Kelley3927da42008-07-20 15:10:39 +01001568 if (lease)
1569 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001570 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001571 if (override.s_addr != 0)
1572 lease->override = override;
1573 else
1574 override = lease->override;
1575 }
1576
Simon Kelleya9df0e32017-04-28 22:43:00 +01001577 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001578 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001579 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001580
1581 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1582 we supply a utility which makes DHCPINFORM requests to get this information.
1583 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1584 which won't be true for ordinary clients, but will be true for the
1585 dhcp_lease_time utility. */
1586 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1587 {
1588 if (lease->expires == 0)
1589 time = 0xffffffff;
1590 else
1591 time = (unsigned int)difftime(lease->expires, now);
1592 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1593 }
1594
Simon Kelley9009d742008-11-14 20:04:27 +00001595 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Wang Shanker4ded9622020-12-04 10:17:35 +08001596 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001597
Simon Kelley5aabfc72007-08-29 11:24:47 +01001598 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001599 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001600 }
1601
1602 return 0;
1603}
1604
Simon Kelley6b010842007-02-12 20:32:07 +00001605/* find a good value to use as MAC address for logging and address-allocation hashing.
1606 This is normally just the chaddr field from the DHCP packet,
1607 but eg Firewire will have hlen == 0 and use the client-id instead.
1608 This could be anything, but will normally be EUI64 for Firewire.
1609 We assume that if the first byte of the client-id equals the htype byte
1610 then the client-id is using the usual encoding and use the rest of the
1611 client-id: if not we can use the whole client-id. This should give
1612 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001613unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001614 int clid_len, unsigned char *clid, int *len_out)
1615{
1616 if (hwlen == 0 && clid && clid_len > 3)
1617 {
1618 if (clid[0] == hwtype)
1619 {
1620 *len_out = clid_len - 1 ;
1621 return clid + 1;
1622 }
1623
1624#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1625 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1626 {
1627 *len_out = clid_len - 1 ;
1628 return clid + 1;
1629 }
1630#endif
1631
1632 *len_out = clid_len;
1633 return clid;
1634 }
1635
1636 *len_out = hwlen;
1637 return hwaddr;
1638}
1639
Simon Kelley824af852008-02-12 20:43:05 +00001640static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001641{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001642 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001643
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001644 if (opt)
1645 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001646 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001647 if (req_time < 120 )
1648 req_time = 120; /* sanity */
1649 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1650 time = req_time;
1651 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001652
1653 return time;
1654}
1655
Simon Kelley73a08a22009-02-05 20:28:08 +00001656static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001657{
Simon Kelley73a08a22009-02-05 20:28:08 +00001658 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001659 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001660 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001661 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001662 else
1663 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001664}
1665
Simon Kelleyf2621c72007-04-29 19:47:21 +01001666static int sanitise(unsigned char *opt, char *buf)
1667{
1668 char *p;
1669 int i;
1670
1671 *buf = 0;
1672
1673 if (!opt)
1674 return 0;
1675
Simon Kelley1a6bca82008-07-11 11:11:42 +01001676 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001677
1678 for (i = option_len(opt); i > 0; i--)
1679 {
1680 char c = *p++;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001681 if (isprint((unsigned char)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001682 *buf++ = c;
1683 }
1684 *buf = 0; /* add terminator */
1685
1686 return 1;
1687}
1688
Simon Kelley316e2732010-01-22 20:16:09 +00001689#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001690static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1691{
1692 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001693 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001694 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001695 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001696}
1697#endif
1698
Simon Kelley5aabfc72007-08-29 11:24:47 +01001699static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001700 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001701{
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001702 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001703 return;
1704
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001705 daemon->addrbuff[0] = 0;
Simon Kelley16972692006-10-16 20:04:18 +01001706 if (addr)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001707 inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
Simon Kelley16972692006-10-16 20:04:18 +01001708
Simon Kelley7622fc02009-06-04 20:32:05 +01001709 print_mac(daemon->namebuff, ext_mac, mac_len);
1710
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001711 if (option_bool(OPT_LOG_OPTS))
1712 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
1713 ntohl(xid),
Simon Kelley7622fc02009-06-04 20:32:05 +01001714 type,
1715 interface,
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001716 daemon->addrbuff,
Simon Kelley7622fc02009-06-04 20:32:05 +01001717 addr ? " " : "",
1718 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001719 string ? string : "",
1720 err ? err : "");
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001721 else
1722 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
1723 type,
1724 interface,
1725 daemon->addrbuff,
1726 addr ? " " : "",
1727 daemon->namebuff,
1728 string ? string : "",
1729 err ? err : "");
1730
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001731#ifdef HAVE_UBUS
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001732 if (!strcmp(type, "DHCPACK"))
1733 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
1734 else if (!strcmp(type, "DHCPRELEASE"))
1735 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
Julian Kornbergercaf4d572018-07-21 21:45:03 +01001736#endif
Simon Kelleyf2621c72007-04-29 19:47:21 +01001737}
1738
Simon Kelley7622fc02009-06-04 20:32:05 +01001739static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001740{
1741 while (*start != OPTION_END)
1742 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001743 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 +01001744
Simon Kelley4cb1b322012-02-06 14:30:41 +00001745 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1746 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001747 start += start[1] + 2;
1748 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001749}
1750
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001751static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001752{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001753 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001754 {
Simon Kelley591ed1e2016-07-11 18:18:42 +01001755 if (p >= end)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001756 return NULL;
1757 else if (*p == OPTION_END)
1758 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001759 else if (*p == OPTION_PAD)
1760 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001761 else
1762 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001763 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001764 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001765 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001766 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001767 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001768 return NULL; /* malformed packet */
1769 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001770 return p;
1771 p += opt_len + 2;
1772 }
1773 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001774}
1775
Simon Kelleycdeda282006-03-16 20:16:06 +00001776static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001777{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001778 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001779
Simon Kelley3be34542004-09-11 19:12:13 +01001780 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001781 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1782 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001783
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001784 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001785 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001786 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001787
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001788 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001789 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001790 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1791 return ret;
1792
1793 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001794 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001795 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1796 return ret;
1797
1798 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001799}
1800
Simon Kelley4cb1b322012-02-06 14:30:41 +00001801static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001802{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001803 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001804 /* struct in_addr is network byte order */
1805 struct in_addr ret;
1806
Simon Kelley4cb1b322012-02-06 14:30:41 +00001807 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001808
1809 return ret;
1810}
1811
Simon Kelley7622fc02009-06-04 20:32:05 +01001812static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001813{
1814 /* this worries about unaligned data and byte order */
1815 unsigned int ret = 0;
1816 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001817 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001818
1819 for (i = 0; i < size; i++)
1820 ret = (ret << 8) | *p++;
1821
1822 return ret;
1823}
1824
1825static unsigned char *dhcp_skip_opts(unsigned char *start)
1826{
1827 while (*start != 0)
1828 start += start[1] + 2;
1829 return start;
1830}
1831
1832/* only for use when building packet: doesn't check for bad data. */
1833static unsigned char *find_overload(struct dhcp_packet *mess)
1834{
1835 unsigned char *p = &mess->options[0] + sizeof(u32);
1836
1837 while (*p != 0)
1838 {
1839 if (*p == OPTION_OVERLOAD)
1840 return p;
1841 p += p[1] + 2;
1842 }
1843 return NULL;
1844}
1845
Simon Kelley7de060b2011-08-26 17:24:52 +01001846static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1847{
1848 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1849 unsigned char *overload;
1850 size_t ret;
1851
1852 /* move agent_id back down to the end of the packet */
1853 if (agent_id)
1854 {
1855 memmove(p, agent_id, real_end - agent_id);
1856 p += real_end - agent_id;
1857 memset(p, 0, real_end - p); /* in case of overlap */
1858 }
1859
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001860 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001861 overload = find_overload(mess);
1862
1863 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001864 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001865 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001866 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001867 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001868 }
Simon Kelley28866e92011-02-14 20:19:14 +00001869 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001870 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1871
1872 if (overload && (option_uint(overload, 0, 1) & 2))
1873 {
1874 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001875 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001876 log_options(mess->sname, mess->xid);
1877 }
Simon Kelley28866e92011-02-14 20:19:14 +00001878 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001879 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1880
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001881
1882 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001883
Simon Kelley28866e92011-02-14 20:19:14 +00001884 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001885 {
1886 if (mess->siaddr.s_addr != 0)
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001887 {
1888 inet_ntop(AF_INET, &mess->siaddr, daemon->addrbuff, ADDRSTRLEN);
1889 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), daemon->addrbuff);
1890 }
Simon Kelley7622fc02009-06-04 20:32:05 +01001891
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001892 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1893 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1894
Simon Kelley7622fc02009-06-04 20:32:05 +01001895 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1896 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001897
1898 ret = (size_t)(p - (unsigned char *)mess);
1899
1900 if (ret < MIN_PACKETSZ)
1901 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001902
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001903 return ret;
1904}
1905
1906static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1907{
1908 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1909
1910 if (p + len + 3 >= end)
1911 /* not enough space in options area, try and use overload, if poss */
1912 {
1913 unsigned char *overload;
1914
1915 if (!(overload = find_overload(mess)) &&
1916 (mess->file[0] == 0 || mess->sname[0] == 0))
1917 {
1918 /* attempt to overload fname and sname areas, we've reserved space for the
1919 overflow option previuously. */
1920 overload = p;
1921 *(p++) = OPTION_OVERLOAD;
1922 *(p++) = 1;
1923 }
1924
1925 p = NULL;
1926
1927 /* using filename field ? */
1928 if (overload)
1929 {
1930 if (mess->file[0] == 0)
1931 overload[2] |= 1;
1932
1933 if (overload[2] & 1)
1934 {
1935 p = dhcp_skip_opts(mess->file);
1936 if (p + len + 3 >= mess->file + sizeof(mess->file))
1937 p = NULL;
1938 }
1939
1940 if (!p)
1941 {
1942 /* try to bring sname into play (it may be already) */
1943 if (mess->sname[0] == 0)
1944 overload[2] |= 2;
1945
1946 if (overload[2] & 2)
1947 {
1948 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001949 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001950 p = NULL;
1951 }
1952 }
1953 }
1954
1955 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001956 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 +00001957 }
1958
1959 if (p)
1960 {
1961 *(p++) = opt;
1962 *(p++) = len;
1963 }
1964
1965 return p;
1966}
1967
1968static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1969{
1970 int i;
1971 unsigned char *p = free_space(mess, end, opt, len);
1972
1973 if (p)
1974 for (i = 0; i < len; i++)
1975 *(p++) = val >> (8 * (len - (i + 1)));
1976}
1977
1978static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
Wang Shanker4ded9622020-12-04 10:17:35 +08001979 const char *string, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001980{
1981 unsigned char *p;
1982 size_t len = strlen(string);
1983
1984 if (null_term && len != 255)
1985 len++;
1986
1987 if ((p = free_space(mess, end, opt, len)))
1988 memcpy(p, string, len);
1989}
1990
1991/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001992static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001993{
1994 int len = opt->len;
1995
1996 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1997 len++;
1998
1999 if (p && len != 0)
2000 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002001 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002002 {
2003 int j;
2004 struct in_addr *a = (struct in_addr *)opt->val;
2005 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
2006 {
2007 /* zero means "self" (but not in vendorclass options.) */
2008 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00002009 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002010 else
2011 memcpy(p, a, INADDRSZ);
2012 p += INADDRSZ;
2013 }
2014 }
2015 else
Simon Kelley625ac282013-07-02 21:19:32 +01002016 /* empty string may be extended to "\0" by null_term */
2017 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002018 }
2019 return len;
2020}
Simon Kelley7622fc02009-06-04 20:32:05 +01002021
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002022static int in_list(unsigned char *list, int opt)
2023{
2024 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00002025
2026 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01002027 if (!list)
2028 return 1;
2029
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002030 for (i = 0; list[i] != OPTION_END; i++)
2031 if (opt == list[i])
2032 return 1;
2033
2034 return 0;
2035}
2036
Simon Kelley7de060b2011-08-26 17:24:52 +01002037static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002038{
Simon Kelley7de060b2011-08-26 17:24:52 +01002039 struct dhcp_opt *opts;
2040
2041 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
2042 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
2043 return opts;
2044
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002045 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002046}
2047
Simon Kelley6b010842007-02-12 20:32:07 +00002048/* mark vendor-encapsulated options which match the client-supplied or
2049 config-supplied vendor class */
2050static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
2051{
2052 for (; dopt; dopt = dopt->next)
2053 {
Simon Kelley7622fc02009-06-04 20:32:05 +01002054 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00002055 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00002056 {
Wang Shanker4ded9622020-12-04 10:17:35 +08002057 const struct dhcp_pxe_vendor *pv;
2058 struct dhcp_pxe_vendor dummy_vendor = {
2059 .data = (char *)dopt->u.vendor_class,
2060 .next = NULL,
2061 };
2062 if (dopt->flags & DHOPT_VENDOR_PXE)
2063 pv = daemon->dhcp_pxe_vendors;
2064 else
2065 pv = &dummy_vendor;
2066 for (; pv; pv = pv->next)
2067 {
2068 int i, len = 0, matched = 0;
2069 if (pv->data)
2070 len = strlen(pv->data);
2071 for (i = 0; i <= (option_len(opt) - len); i++)
2072 if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0)
2073 {
2074 matched = 1;
2075 break;
2076 }
2077 if (matched)
2078 {
2079 dopt->flags |= DHOPT_VENDOR_MATCH;
2080 break;
2081 }
2082 }
Simon Kelley6b010842007-02-12 20:32:07 +00002083 }
2084 }
2085}
2086
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002087static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
2088 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00002089{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002090 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01002091 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00002092 unsigned char *p;
2093
2094 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01002095 for (enc_len = 0, start = opt; opt; opt = opt->next)
2096 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002097 {
2098 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002099 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00002100 if (enc_len + new <= 255)
2101 enc_len += new;
2102 else
2103 {
2104 p = free_space(mess, end, encap, enc_len);
2105 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002106 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00002107 {
2108 len = do_opt(start, p + 2, NULL, null_term);
2109 *(p++) = start->opt;
2110 *(p++) = len;
2111 p += len;
2112 }
2113 enc_len = new;
2114 start = opt;
2115 }
2116 }
2117
2118 if (enc_len != 0 &&
2119 (p = free_space(mess, end, encap, enc_len + 1)))
2120 {
2121 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002122 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002123 {
2124 len = do_opt(start, p + 2, NULL, null_term);
2125 *(p++) = start->opt;
2126 *(p++) = len;
2127 p += len;
2128 }
2129 *p = OPTION_END;
2130 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002131
2132 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002133}
2134
Wang Shanker4ded9622020-12-04 10:17:35 +08002135static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor)
Simon Kelley91dccd02005-03-31 17:48:32 +01002136{
Simon Kelley7622fc02009-06-04 20:32:05 +01002137 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002138
Wang Shanker4ded9622020-12-04 10:17:35 +08002139 if (!pxevendor)
2140 pxevendor="PXEClient";
2141 option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002142 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2143 memcpy(p, uuid, 17);
2144}
2145
2146static int prune_vendor_opts(struct dhcp_netid *netid)
2147{
2148 int force = 0;
2149 struct dhcp_opt *opt;
2150
2151 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2152 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2153 if (opt->flags & DHOPT_VENDOR_MATCH)
2154 {
2155 if (!match_netid(opt->netid, netid, 1))
2156 opt->flags &= ~DHOPT_VENDOR_MATCH;
2157 else if (opt->flags & DHOPT_FORCE)
2158 force = 1;
2159 }
2160 return force;
2161}
2162
Simon Kelley8628cd62016-05-10 17:31:48 +01002163
2164/* Many UEFI PXE implementations have badly broken menu code.
2165 If there's exactly one relevant menu item, we abandon the menu system,
2166 and jamb the data direct into the DHCP file, siaddr and sname fields.
2167 Note that in this case, we have to assume that layer zero would be requested
2168 by the client PXE stack. */
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002169static 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 +01002170{
2171 struct pxe_service *service, *found;
2172
2173 /* Only workaround UEFI archs. */
Simon Kelleycbc100f2016-05-11 22:17:18 +01002174 if (pxe_arch < 6)
Simon Kelley8628cd62016-05-10 17:31:48 +01002175 return 0;
2176
2177 for (found = NULL, service = daemon->pxe_services; service; service = service->next)
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002178 if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
Simon Kelley8628cd62016-05-10 17:31:48 +01002179 {
2180 if (found)
2181 return 0; /* More than one relevant menu item */
2182
2183 found = service;
2184 }
2185
2186 if (!found)
2187 return 0; /* No relevant menu items. */
2188
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002189 if (!pxe)
2190 return 1;
2191
Simon Kelley8628cd62016-05-10 17:31:48 +01002192 if (found->sname)
2193 {
2194 mess->siaddr = a_record_from_hosts(found->sname, now);
2195 snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
2196 }
2197 else
2198 {
2199 if (found->server.s_addr != 0)
2200 mess->siaddr = found->server;
2201 else
2202 mess->siaddr = local;
2203
2204 inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
2205 }
2206
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002207 if (found->basename)
2208 snprintf((char *)mess->file, sizeof(mess->file),
2209 strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002210
Simon Kelley8628cd62016-05-10 17:31:48 +01002211 return 1;
2212}
2213
Simon Kelley751d6f42012-02-10 15:24:51 +00002214static 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 +01002215{
2216#define NUM_OPTS 4
2217
2218 unsigned char *p, *q;
2219 struct pxe_service *service;
2220 static struct dhcp_opt *o, *ret;
2221 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002222 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002223
2224 /* We pass back references to these, hence they are declared static */
2225 static unsigned char discovery_control;
2226 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2227 static struct dhcp_opt *fake_opts = NULL;
2228
Simon Kelley316e2732010-01-22 20:16:09 +00002229 /* Disable multicast, since we don't support it, and broadcast
2230 unless we need it */
2231 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002232
2233 ret = daemon->dhcp_opts;
2234
2235 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2236 return ret;
2237
2238 for (i = 0; i < NUM_OPTS; i++)
2239 {
2240 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2241 fake_opts[i].netid = NULL;
2242 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2243 }
2244
2245 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2246 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002247 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002248
2249 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2250 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2251 {
2252 size_t len = strlen(service->menu);
2253 /* opt 43 max size is 255. encapsulated option has type and length
2254 bytes, so its max size is 253. */
2255 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2256 {
2257 *(p++) = service->type >> 8;
2258 *(p++) = service->type;
2259 *(p++) = len;
2260 memcpy(p, service->menu, len);
2261 p += len;
2262 i++;
2263 }
2264 else
2265 {
2266 toobig:
2267 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2268 return daemon->dhcp_opts;
2269 }
2270
Simon Kelley751d6f42012-02-10 15:24:51 +00002271 boot_server = service->basename ? local :
2272 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2273
Simon Kelley316e2732010-01-22 20:16:09 +00002274 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002275 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002276 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002277 goto toobig;
2278
2279 /* Boot service with known address - give it */
2280 *(q++) = service->type >> 8;
2281 *(q++) = service->type;
2282 *(q++) = 1;
2283 /* dest misaligned */
2284 memcpy(q, &boot_server.s_addr, INADDRSZ);
2285 q += INADDRSZ;
2286 }
2287 else if (service->type != 0)
2288 /* We don't know the server for a service type, so we'll
2289 allow the client to broadcast for it */
2290 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002291 }
2292
2293 /* if no prompt, wait forever if there's a choice */
2294 fake_prompt[0] = (i > 1) ? 255 : 0;
2295
2296 if (i == 0)
2297 discovery_control = 8; /* no menu - just use use mess->filename */
2298 else
2299 {
2300 ret = &fake_opts[j--];
2301 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2302 ret->val = (unsigned char *)daemon->dhcp_buff;
2303 ret->opt = SUBOPT_PXE_MENU;
2304
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002305 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002306 {
2307 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002308 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2309 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002310 ret->opt = SUBOPT_PXE_SERVERS;
2311 }
2312 }
2313
2314 for (o = daemon->dhcp_opts; o; o = o->next)
2315 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2316 break;
2317
2318 if (!o)
2319 {
2320 ret = &fake_opts[j--];
2321 ret->len = sizeof(fake_prompt);
2322 ret->val = fake_prompt;
2323 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2324 }
2325
Simon Kelley316e2732010-01-22 20:16:09 +00002326 ret = &fake_opts[j--];
2327 ret->len = 1;
2328 ret->opt = SUBOPT_PXE_DISCOVERY;
2329 ret->val= &discovery_control;
2330
Simon Kelley7622fc02009-06-04 20:32:05 +01002331 return ret;
2332}
Simon Kelleya9df0e32017-04-28 22:43:00 +01002333
2334static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley7622fc02009-06-04 20:32:05 +01002335{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002336 memset(mess->sname, 0, sizeof(mess->sname));
2337 memset(mess->file, 0, sizeof(mess->file));
Simon Kelleya9df0e32017-04-28 22:43:00 +01002338 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002339 mess->siaddr.s_addr = 0;
2340}
Simon Kelleycdeda282006-03-16 20:16:06 +00002341
Simon Kelley7622fc02009-06-04 20:32:05 +01002342struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002343{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002344 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002345
2346 /* decide which dhcp-boot option we're using */
2347 for (boot = daemon->boot_config; boot; boot = boot->next)
2348 if (match_netid(boot->netid, netid, 0))
2349 break;
2350 if (!boot)
2351 /* No match, look for one without a netid */
2352 for (boot = daemon->boot_config; boot; boot = boot->next)
2353 if (match_netid(boot->netid, netid, 1))
2354 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002355
2356 return boot;
2357}
2358
Wang Shanker4ded9622020-12-04 10:17:35 +08002359static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor)
2360{
2361 const unsigned char *opt = NULL;
2362 ssize_t conf_len = 0;
2363 const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors;
2364 opt = option_find(mess, sz, OPTION_VENDOR_ID, 0);
2365 if (!opt)
2366 return 0;
2367 for (; conf; conf = conf->next)
2368 {
2369 conf_len = strlen(conf->data);
2370 if (option_len(opt) < conf_len)
2371 continue;
2372 if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0)
2373 {
2374 if (pxe_vendor)
2375 *pxe_vendor = conf->data;
2376 return 1;
2377 }
2378 }
2379 return 0;
2380}
2381
Simon Kelley7622fc02009-06-04 20:32:05 +01002382static void do_options(struct dhcp_context *context,
2383 struct dhcp_packet *mess,
2384 unsigned char *end,
2385 unsigned char *req_options,
2386 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002387 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002388 struct dhcp_netid *netid,
2389 struct in_addr subnet_addr,
2390 unsigned char fqdn_flags,
2391 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002392 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002393 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002394 time_t now,
2395 unsigned int lease_time,
Wang Shanker4ded9622020-12-04 10:17:35 +08002396 unsigned short fuzz,
2397 const char *pxevendor)
Simon Kelley7622fc02009-06-04 20:32:05 +01002398{
2399 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2400 struct dhcp_boot *boot;
2401 unsigned char *p;
2402 int i, len, force_encap = 0;
2403 unsigned char f0 = 0, s0 = 0;
2404 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002405 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002406 struct dhcp_netid *tagif;
2407 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002408
Simon Kelley4cb1b322012-02-06 14:30:41 +00002409 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002410 if (context)
2411 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002412 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002413
Simon Kelley7622fc02009-06-04 20:32:05 +01002414 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002415 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002416 {
2417 char *q = daemon->namebuff;
2418 for (i = 0; req_options[i] != OPTION_END; i++)
2419 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002420 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002421 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2422 "%d%s%s%s",
2423 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002424 strlen(s) != 0 ? ":" : "",
2425 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002426 req_options[i+1] == OPTION_END ? "" : ", ");
2427 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2428 {
2429 q = daemon->namebuff;
2430 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2431 }
2432 }
2433 }
2434
Simon Kelley7de060b2011-08-26 17:24:52 +01002435 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2436 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2437 break;
2438 if (id_list)
2439 mess->flags |= htons(0x8000); /* force broadcast */
2440
Simon Kelley73a08a22009-02-05 20:28:08 +00002441 if (context)
2442 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002443
2444 /* See if we can send the boot stuff as options.
2445 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002446 and very old DHCP clients won't have this, we also
Ville Skyttäfaaf3062018-01-14 17:32:52 +00002447 provide a manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002448 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002449 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002450 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002451 {
2452 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002453 {
Simon Kelley28866e92011-02-14 20:19:14 +00002454 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002455 req_options &&
2456 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002457 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2458 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002459 safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002460 }
2461
2462 if (boot->file)
2463 {
Simon Kelley28866e92011-02-14 20:19:14 +00002464 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002465 req_options &&
2466 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002467 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2468 else
Petr Menšík47b45b22018-08-15 18:17:00 +02002469 safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002470 }
2471
Simon Kelley7de060b2011-08-26 17:24:52 +01002472 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002473 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002474 else if (boot->tftp_sname)
2475 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002476 }
Simon Kelley824af852008-02-12 20:43:05 +00002477 else
2478 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002479 they're not explicitly asked for as options. OPTION_END is used
2480 as an internal way to specify siaddr without using dhcp-boot, for use in
2481 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002482 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002483 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002484 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002485 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002486 safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
Simon Kelley824af852008-02-12 20:43:05 +00002487 done_file = 1;
2488 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002489
Simon Kelley824af852008-02-12 20:43:05 +00002490 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002491 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002492 {
Petr Menšík47b45b22018-08-15 18:17:00 +02002493 safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
Simon Kelley824af852008-02-12 20:43:05 +00002494 done_server = 1;
2495 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002496
Simon Kelley7de060b2011-08-26 17:24:52 +01002497 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002498 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002499 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002500
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002501 /* We don't want to do option-overload for BOOTP, so make the file and sname
2502 fields look like they are in use, even when they aren't. This gets restored
2503 at the end of this function. */
2504
Simon Kelley28866e92011-02-14 20:19:14 +00002505 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002506 {
2507 f0 = mess->file[0];
2508 mess->file[0] = 1;
2509 s0 = mess->sname[0];
2510 mess->sname[0] = 1;
2511 }
2512
2513 /* At this point, if mess->sname or mess->file are zeroed, they are available
2514 for option overload, reserve space for the overload option. */
2515 if (mess->file[0] == 0 || mess->sname[0] == 0)
2516 end -= 3;
2517
Simon Kelley3be34542004-09-11 19:12:13 +01002518 /* rfc3011 says this doesn't need to be in the requested options list. */
2519 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002520 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002521
2522 if (lease_time != 0xffffffff)
2523 {
2524 unsigned int t1val = lease_time/2;
2525 unsigned int t2val = (lease_time*7)/8;
2526 unsigned int hval;
2527
2528 /* If set by user, sanity check, so not longer than lease. */
2529 if ((opt = option_find2(OPTION_T1)))
2530 {
2531 hval = ntohl(*((unsigned int *)opt->val));
2532 if (hval < lease_time && hval > 2)
2533 t1val = hval;
2534 }
2535
2536 if ((opt = option_find2(OPTION_T2)))
2537 {
2538 hval = ntohl(*((unsigned int *)opt->val));
2539 if (hval < lease_time && hval > 2)
2540 t2val = hval;
2541 }
2542
Simon Kelley7c0f2542015-05-14 21:16:18 +01002543 /* ensure T1 is still < T2 */
2544 if (t2val <= t1val)
2545 t1val = t2val - 1;
2546
Simon Kelleyca85a282015-05-13 22:33:04 +01002547 while (fuzz > (t1val/8))
2548 fuzz = fuzz/2;
2549
2550 t1val -= fuzz;
2551 t2val -= fuzz;
2552
Simon Kelleyca85a282015-05-13 22:33:04 +01002553 option_put(mess, end, OPTION_T1, 4, t1val);
2554 option_put(mess, end, OPTION_T2, 4, t2val);
2555 }
2556
Simon Kelley73a08a22009-02-05 20:28:08 +00002557 /* replies to DHCPINFORM may not have a valid context */
2558 if (context)
2559 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002560 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002561 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2562
2563 /* May not have a "guessed" broadcast address if we got no packets via a relay
2564 from this net yet (ie just unicast renewals after a restart */
2565 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002566 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002567 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2568
2569 /* Same comments as broadcast apply, and also may not be able to get a sensible
2570 default when using subnet select. User must configure by steam in that case. */
2571 if (context->router.s_addr &&
2572 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002573 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002574 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2575
Simon Kelleya21e27b2013-02-17 16:41:35 +00002576 if (daemon->port == NAMESERVER_PORT &&
2577 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002578 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002579 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2580 }
Simon Kelley3be34542004-09-11 19:12:13 +01002581
Simon Kelley9009d742008-11-14 20:04:27 +00002582 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002583 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002584 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002585
Simon Kelley824af852008-02-12 20:43:05 +00002586 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002587 if (hostname)
2588 {
Simon Kelley824af852008-02-12 20:43:05 +00002589 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002590 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002591 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002592
2593 if (fqdn_flags != 0)
2594 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002595 len = strlen(hostname) + 3;
2596
Simon Kelley3d8df262005-08-29 12:19:27 +01002597 if (fqdn_flags & 0x04)
2598 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002599 else if (null_term)
2600 len++;
2601
Simon Kelley9009d742008-11-14 20:04:27 +00002602 if (domain)
2603 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002604 else if (fqdn_flags & 0x04)
2605 len--;
2606
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002607 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002608 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002609 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002610 *(p++) = 255;
2611 *(p++) = 255;
2612
2613 if (fqdn_flags & 0x04)
2614 {
Simon Kelley0549c732017-09-25 18:17:11 +01002615 p = do_rfc1035_name(p, hostname, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +00002616 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002617 {
Simon Kelley0549c732017-09-25 18:17:11 +01002618 p = do_rfc1035_name(p, domain, NULL);
Roy Marples3f3adae2013-07-25 16:22:46 +01002619 *p++ = 0;
2620 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002621 }
2622 else
2623 {
2624 memcpy(p, hostname, strlen(hostname));
2625 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002626 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002627 {
2628 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002629 memcpy(p, domain, strlen(domain));
2630 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002631 }
2632 if (null_term)
2633 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002634 }
2635 }
2636 }
2637 }
2638
Simon Kelley6b010842007-02-12 20:32:07 +00002639 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002640 {
Simon Kelley824af852008-02-12 20:43:05 +00002641 int optno = opt->opt;
2642
Simon Kelley7de060b2011-08-26 17:24:52 +01002643 /* netids match and not encapsulated? */
2644 if (!(opt->flags & DHOPT_TAGOK))
2645 continue;
2646
Simon Kelley6b010842007-02-12 20:32:07 +00002647 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002648 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002649 continue;
2650
Simon Kelleyca85a282015-05-13 22:33:04 +01002651 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002652 if (optno == OPTION_CLIENT_FQDN ||
2653 optno == OPTION_MAXMESSAGE ||
2654 optno == OPTION_OVERLOAD ||
2655 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002656 optno == OPTION_END ||
2657 optno == OPTION_T1 ||
2658 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002659 continue;
2660
2661 if (optno == OPTION_SNAME && done_server)
2662 continue;
2663
2664 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002665 continue;
2666
Simon Kelley33820b72004-04-03 21:10:00 +01002667 /* For the options we have default values on
2668 dhc-option=<optionno> means "don't include this option"
2669 not "include a zero-length option" */
2670 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002671 (optno == OPTION_NETMASK ||
2672 optno == OPTION_BROADCAST ||
2673 optno == OPTION_ROUTER ||
2674 optno == OPTION_DNSSERVER ||
2675 optno == OPTION_DOMAINNAME ||
2676 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002677 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002678
2679 /* vendor-class comes from elsewhere for PXE */
2680 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2681 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002682
Simon Kelley7622fc02009-06-04 20:32:05 +01002683 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002684 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002685 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002686
Simon Kelley824af852008-02-12 20:43:05 +00002687 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002688 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002689 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002690 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2691
Simon Kelley6b010842007-02-12 20:32:07 +00002692 /* If we send a vendor-id, revisit which vendor-ops we consider
2693 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002694 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002695 {
2696 match_vendor_opts(p - 2, config_opts);
2697 done_vendor_class = 1;
2698 }
Simon Kelley6b010842007-02-12 20:32:07 +00002699 }
2700 }
2701
Simon Kelley73a08a22009-02-05 20:28:08 +00002702 /* Now send options to be encapsulated in arbitrary options,
2703 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002704 Also handle vendor-identifying vendor-encapsulated options,
2705 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002706 The may be more that one "outer" to do, so group
2707 all the options which match each outer in turn. */
2708 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002709 opt->flags &= ~DHOPT_ENCAP_DONE;
2710
2711 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002712 {
2713 int flags;
2714
2715 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2716 {
2717 int found = 0;
2718 struct dhcp_opt *o;
2719
2720 if (opt->flags & DHOPT_ENCAP_DONE)
2721 continue;
2722
2723 for (len = 0, o = config_opts; o; o = o->next)
2724 {
2725 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2726
2727 o->flags &= ~DHOPT_ENCAP_MATCH;
2728
2729 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2730 continue;
2731
2732 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002733 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002734 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2735 {
2736 o->flags |= DHOPT_ENCAP_MATCH;
2737 found = 1;
2738 len += do_opt(o, NULL, NULL, 0) + 2;
2739 }
2740 }
2741
2742 if (found)
2743 {
2744 if (flags & DHOPT_ENCAPSULATE)
2745 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2746 else if (len > 250)
2747 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2748 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2749 {
2750 int swap_ent = htonl(opt->u.encap);
2751 memcpy(p, &swap_ent, 4);
2752 p += 4;
2753 *(p++) = len;
2754 for (o = config_opts; o; o = o->next)
2755 if (o->flags & DHOPT_ENCAP_MATCH)
2756 {
2757 len = do_opt(o, p + 2, NULL, 0);
2758 *(p++) = o->opt;
2759 *(p++) = len;
2760 p += len;
2761 }
2762 }
2763 }
2764 }
2765 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002766
Simon Kelley7de060b2011-08-26 17:24:52 +01002767 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002768
Simon Kelley316e2732010-01-22 20:16:09 +00002769 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002770 {
Wang Shanker4ded9622020-12-04 10:17:35 +08002771 pxe_misc(mess, end, uuid, pxevendor);
Simon Kelleyfe71bba2016-05-14 20:50:45 +01002772 if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
Simon Kelley8628cd62016-05-10 17:31:48 +01002773 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002774 }
2775
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002776 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2777 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2778 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2779 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2780 /* If we send vendor encapsulated options, and haven't already sent option 60,
2781 echo back the value we got from the client. */
2782 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2783
Simon Kelley7622fc02009-06-04 20:32:05 +01002784 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002785 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002786 {
2787 mess->file[0] = f0;
2788 mess->sname[0] = s0;
2789 }
2790}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002791
Floris Bos503c6092017-04-09 23:07:13 +01002792static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
2793{
2794 struct delay_config *delay_conf;
2795
2796 /* Decide which delay_config option we're using */
2797 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2798 if (match_netid(delay_conf->netid, netid, 0))
2799 break;
2800
2801 if (!delay_conf)
2802 /* No match, look for one without a netid */
2803 for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
2804 if (match_netid(delay_conf->netid, netid, 1))
2805 break;
2806
2807 if (delay_conf)
2808 {
2809 if (!option_bool(OPT_QUIET_DHCP))
2810 my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
2811 delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
2812 }
2813}
2814
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07002815#endif /* HAVE_DHCP */