blob: 9f69ed5959032cdac7b9a7f480ff8fe84b57699b [file] [log] [blame]
Simon Kelleyaff33962015-01-31 20:13:40 +00001/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010021#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010022#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000023
Simon Kelley316e2732010-01-22 20:16:09 +000024#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +000025static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
26#endif
Simon Kelley572b41e2011-02-18 18:11:18 +000027
Simon Kelleyf2621c72007-04-29 19:47:21 +010028static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000029static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000030static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000031static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
32static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
33 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034static struct in_addr option_addr(unsigned char *opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010035static unsigned int option_uint(unsigned char *opt, int i, int size);
36static 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 Kelley7622fc02009-06-04 20:32:05 +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,
45 unsigned char *real_end,
46 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000047 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +000048 char *config_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,
Simon Kelley7622fc02009-06-04 20:32:05 +010052 int null_term, int pxearch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +010053 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +010054 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +010055 time_t now,
56 unsigned int lease_time,
57 unsigned short fuzz);
Simon Kelley7622fc02009-06-04 20:32:05 +010058
Simon Kelley9009d742008-11-14 20:04:27 +000059
Simon Kelley6b010842007-02-12 20:32:07 +000060static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Simon Kelley8ef5ada2010-06-03 19:42:45 +010061static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
Simon Kelley7622fc02009-06-04 20:32:05 +010062static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
63static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000064static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010065struct dhcp_boot *find_boot(struct dhcp_netid *netid);
66
67
Simon Kelley824af852008-02-12 20:43:05 +000068size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley7de060b2011-08-26 17:24:52 +010069 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
Simon Kelley33820b72004-04-03 21:10:00 +010070{
Simon Kelley26128d22004-11-14 16:43:54 +000071 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000072 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010073 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000074 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000075 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +010076 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000077 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000078 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010079 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000080 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000081 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010082 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000083 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000084 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000085 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010086 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010087 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010088 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010089 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010090 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010091 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000092 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010093 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000094 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000095 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010096 unsigned char pxe_uuid[17];
Vladislav Grishenko99e88912013-11-26 11:02:29 +000097 unsigned char *oui = NULL, *serial = NULL;
98#ifdef HAVE_SCRIPT
99 unsigned char *class = NULL;
100#endif
Simon Kelley3be34542004-09-11 19:12:13 +0100101
Simon Kelley1a6bca82008-07-11 11:11:42 +0100102 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000103
104 /* set tag with name == interface */
105 iface_id.net = iface_name;
106 iface_id.next = NULL;
107 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100108
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100109 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100110 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000111
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100112 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000113 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100114
Simon Kelley3be34542004-09-11 19:12:13 +0100115 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000116 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000117 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100118 u32 cookie = htonl(DHCP_COOKIE);
119
Simon Kelley3be34542004-09-11 19:12:13 +0100120 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100121 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100122 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100123
124 mess_type = option_uint(opt, 0, 1);
125
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100126 /* two things to note here: expand_buf may move the packet,
127 so reassign mess from daemon->packet. Also, the size
128 sent includes the IP and UDP headers, hence the magic "-28" */
129 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
130 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100131 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100132
133 if (size > DHCP_PACKET_MAX)
134 size = DHCP_PACKET_MAX;
135 else if (size < sizeof(struct dhcp_packet))
136 size = sizeof(struct dhcp_packet);
137
138 if (expand_buf(&daemon->dhcp_packet, size))
139 {
Simon Kelley824af852008-02-12 20:43:05 +0000140 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100141 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100142 }
143 }
144
Simon Kelley3be34542004-09-11 19:12:13 +0100145 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
146 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000147 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100148 mess->ciaddr.s_addr = 0;
149
Simon Kelley316e2732010-01-22 20:16:09 +0000150 /* search for device identity from CPEWAN devices, we pass this through to the script */
151 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
152 {
153 unsigned int elen, offset, len = option_len(opt);
154
155 for (offset = 0; offset < (len - 5); offset += elen + 5)
156 {
157 elen = option_uint(opt, offset + 4 , 1);
158 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
159 {
160 unsigned char *x = option_ptr(opt, offset + 5);
161 unsigned char *y = option_ptr(opt, offset + elen + 5);
162 oui = option_find1(x, y, 1, 1);
163 serial = option_find1(x, y, 2, 1);
Vladislav Grishenko99e88912013-11-26 11:02:29 +0000164#ifdef HAVE_SCRIPT
165 class = option_find1(x, y, 3, 1);
166#endif
Simon Kelley316e2732010-01-22 20:16:09 +0000167 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
168 the gateway id back. Note that the device class is optional */
169 if (oui && serial)
170 {
171 cpewan_id.net = "cpewan-id";
172 cpewan_id.next = netid;
173 netid = &cpewan_id;
174 }
175 break;
176 }
177 }
178 }
179
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100180 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
181 {
182 /* Any agent-id needs to be copied back out, verbatim, as the last option
183 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
184 get overwritten, then it will be shuffled back at the end of processing.
185 Note that the incoming options must not be overwritten here, so there has to
186 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100187 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100188 unsigned int total = option_len(opt) + 2;
189 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
190 if (last_opt && last_opt < end - total)
191 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100192 end -= total;
193 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100194 memcpy(agent_id, opt, total);
195 }
196
197 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100198 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 +0100199 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100200
201 /* look for RFC5107 server-identifier-override */
202 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
203 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100204
205 /* if a circuit-id or remote-is option is provided, exact-match to options. */
206 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
207 {
208 int search;
209
210 if (vendor->match_type == MATCH_CIRCUIT)
211 search = SUBOPT_CIRCUIT_ID;
212 else if (vendor->match_type == MATCH_REMOTE)
213 search = SUBOPT_REMOTE_ID;
214 else if (vendor->match_type == MATCH_SUBSCRIBER)
215 search = SUBOPT_SUBSCR_ID;
216 else
217 continue;
218
Simon Kelley1a6bca82008-07-11 11:11:42 +0100219 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100220 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100221 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100222 {
223 vendor->netid.next = netid;
224 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100225 }
226 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100227 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100228
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100229 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
230 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100231 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000232
233 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000234 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000235 {
Simon Kelley26128d22004-11-14 16:43:54 +0000236 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100237 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000238 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000239
Simon Kelley0a852542005-03-23 20:28:59 +0000240 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100241 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000242
243 /* If this request is missing a clid, but we've seen one before,
244 use it again for option matching etc. */
245 if (lease && !clid && lease->clid)
246 {
247 clid_len = lease->clid_len;
248 clid = lease->clid;
249 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000250
251 /* find mac to use for logging and hashing */
252 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000253 }
Simon Kelley3be34542004-09-11 19:12:13 +0100254
Simon Kelleycdeda282006-03-16 20:16:06 +0000255 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
256 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100257 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
258 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000259 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100260 mac->netid.next = netid;
261 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000262 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100263
Simon Kelley0a852542005-03-23 20:28:59 +0000264 /* Determine network for this packet. Our caller will have already linked all the
265 contexts which match the addresses of the receiving interface but if the
266 machine has an address already, or came via a relay, or we have a subnet selector,
267 we search again. If we don't have have a giaddr or explicit subnet selector,
268 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100269 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
270 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000271 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
272 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100273 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100274 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100275 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000276
Simon Kelley3d8df262005-08-29 12:19:27 +0100277 if (subnet_addr.s_addr)
278 {
279 addr = subnet_addr;
280 force = 1;
281 }
282 else if (mess->giaddr.s_addr)
283 {
284 addr = mess->giaddr;
285 force = 1;
286 }
Simon Kelley16972692006-10-16 20:04:18 +0100287 else
288 {
289 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
290 addr = mess->ciaddr;
291 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
292 if (context_tmp->netmask.s_addr &&
293 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
294 is_same_net(addr, context_tmp->end, context_tmp->netmask))
295 {
296 context_new = context;
297 break;
298 }
299 }
300
301 if (!context_new)
302 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100303 {
304 struct in_addr netmask = context_tmp->netmask;
305
306 /* guess the netmask for relayed networks */
307 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
308 {
309 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
310 netmask.s_addr = htonl(0xff000000);
311 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
312 netmask.s_addr = htonl(0xffff0000);
313 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
314 netmask.s_addr = htonl(0xffffff00);
315 }
316
317 /* This section fills in context mainly when a client which is on a remote (relayed)
318 network renews a lease without using the relay, after dnsmasq has restarted. */
319 if (netmask.s_addr != 0 &&
320 is_same_net(addr, context_tmp->start, netmask) &&
321 is_same_net(addr, context_tmp->end, netmask))
322 {
323 context_tmp->netmask = netmask;
324 if (context_tmp->local.s_addr == 0)
325 context_tmp->local = fallback;
326 if (context_tmp->router.s_addr == 0)
327 context_tmp->router = mess->giaddr;
328
329 /* fill in missing broadcast addresses for relayed ranges */
330 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
331 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
332
333 context_tmp->current = context_new;
334 context_new = context_tmp;
335 }
336 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100337
Simon Kelley3d8df262005-08-29 12:19:27 +0100338 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100339 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000340 }
Simon Kelley3be34542004-09-11 19:12:13 +0100341
342 if (!context)
343 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100344 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100345 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
346 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
Simon Kelley3be34542004-09-11 19:12:13 +0100347 return 0;
348 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100349
Simon Kelley28866e92011-02-14 20:19:14 +0000350 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100351 {
352 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100353 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
354 {
355 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100356 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100357 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100358 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100359 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100360 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100361 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100362 }
363 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100364
365 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
366 Otherwise assume the option is an array, and look for a matching element.
367 If no data given, existance of the option is enough. This code handles
368 rfc3925 V-I classes too. */
369 for (o = daemon->dhcp_match; o; o = o->next)
370 {
371 unsigned int len, elen, match = 0;
372 size_t offset, o2;
373
374 if (o->flags & DHOPT_RFC3925)
375 {
376 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
377 continue;
378
379 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
380 {
381 len = option_uint(opt, offset + 4 , 1);
382 /* Need to take care that bad data can't run us off the end of the packet */
383 if ((offset + len + 5 <= (option_len(opt))) &&
384 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
385 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
386 {
387 elen = option_uint(opt, o2, 1);
388 if ((o2 + elen + 1 <= option_len(opt)) &&
389 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
390 break;
391 }
392 if (match)
393 break;
394 }
395 }
396 else
397 {
398 if (!(opt = option_find(mess, sz, o->opt, 1)))
399 continue;
400
401 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
402 }
403
404 if (match)
405 {
406 o->netid->next = netid;
407 netid = o->netid;
408 }
409 }
410
411 /* user-class options are, according to RFC3004, supposed to contain
412 a set of counted strings. Here we check that this is so (by seeing
413 if the counts are consistent with the overall option length) and if
414 so zero the counts so that we don't get spurious matches between
415 the vendor string and the counts. If the lengths don't add up, we
416 assume that the option is a single string and non RFC3004 compliant
417 and just do the substring match. dhclient provides these broken options.
418 The code, later, which sends user-class data to the lease-change script
419 relies on the transformation done here.
420 */
421
422 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
423 {
424 unsigned char *ucp = option_ptr(opt, 0);
425 int tmp, j;
426 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
427 if (j == option_len(opt))
428 for (j = 0; j < option_len(opt); j = tmp)
429 {
430 tmp = j + ucp[j] + 1;
431 ucp[j] = 0;
432 }
433 }
434
435 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
436 {
437 int mopt;
438
439 if (vendor->match_type == MATCH_VENDOR)
440 mopt = OPTION_VENDOR_ID;
441 else if (vendor->match_type == MATCH_USER)
442 mopt = OPTION_USER_CLASS;
443 else
444 continue;
445
446 if ((opt = option_find(mess, sz, mopt, 1)))
447 {
448 int i;
449 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
450 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
451 {
452 vendor->netid.next = netid;
453 netid = &vendor->netid;
454 break;
455 }
456 }
457 }
458
459 /* mark vendor-encapsulated options which match the client-supplied vendor class,
460 save client-supplied vendor class */
461 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
462 {
463 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
464 vendor_class_len = option_len(opt);
465 }
466 match_vendor_opts(opt, daemon->dhcp_opts);
467
468 if (option_bool(OPT_LOG_OPTS))
469 {
470 if (sanitise(opt, daemon->namebuff))
471 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
472 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
473 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
474 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100475
Simon Kelley3be34542004-09-11 19:12:13 +0100476 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100477
Simon Kelleycdeda282006-03-16 20:16:06 +0000478 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
479 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100480
481 /* set "known" tag for known hosts */
482 if (config)
483 {
484 known_id.net = "known";
485 known_id.next = netid;
486 netid = &known_id;
487 }
Simon Kelley26128d22004-11-14 16:43:54 +0000488
Simon Kelley316e2732010-01-22 20:16:09 +0000489 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100490 {
491 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000492 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000493 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100494
495 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100496 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100497 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000498
Simon Kelley26128d22004-11-14 16:43:54 +0000499 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000500 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000501
502 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
503
504 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000505 {
506 hostname = config->hostname;
507 domain = config->domain;
508 }
509
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100510 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000511 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100512 struct dhcp_netid_list *list;
513
514 for (list = config->netid; list; list = list->next)
515 {
516 list->list->next = netid;
517 netid = list->list;
518 }
Simon Kelley26128d22004-11-14 16:43:54 +0000519 }
520
521 /* Match incoming filename field as a netid. */
522 if (mess->file[0])
523 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000524 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
525 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
526 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000527 id.next = netid;
528 netid = &id;
529 }
Simon Kelley6b010842007-02-12 20:32:07 +0000530
531 /* Add "bootp" as a tag to allow different options, address ranges etc
532 for BOOTP clients */
533 bootp_id.net = "bootp";
534 bootp_id.next = netid;
535 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000536
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100537 tagif_netid = run_tag_if(netid);
538
Simon Kelley26128d22004-11-14 16:43:54 +0000539 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100540 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100541 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000542
Simon Kelley3d8df262005-08-29 12:19:27 +0100543 if (!message)
544 {
Simon Kelley9009d742008-11-14 20:04:27 +0000545 int nailed = 0;
546
Simon Kelley3d8df262005-08-29 12:19:27 +0100547 if (have_config(config, CONFIG_ADDR))
548 {
Simon Kelley9009d742008-11-14 20:04:27 +0000549 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100550 logaddr = &config->addr;
551 mess->yiaddr = config->addr;
552 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000553 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100554 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000555 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000556 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100557 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100558 else
559 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000560 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100561 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000562 {
563 if (lease)
564 {
565 /* lease exists, wrong network. */
566 lease_prune(lease, now);
567 lease = NULL;
568 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100569 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000570 message = _("no address available");
571 }
572 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100573 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100574 }
575
Simon Kelley9009d742008-11-14 20:04:27 +0000576 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
577 message = _("wrong network");
578 else if (context->netid.net)
579 {
580 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100581 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100582 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100583
Simon Kelley4cb1b322012-02-06 14:30:41 +0000584 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100585
Simon Kelley9009d742008-11-14 20:04:27 +0000586 if (!message && !nailed)
587 {
588 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100589 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000590 break;
591 if (!id_list)
592 message = _("no address configured");
593 }
594
Simon Kelley7cebd202006-05-06 14:13:33 +0100595 if (!message &&
596 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000597 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000598 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000599
Simon Kelley3d8df262005-08-29 12:19:27 +0100600 if (!message)
601 {
602 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100603
Simon Kelleya9ab7322012-04-28 11:29:37 +0100604 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100605 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000606 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100607 /* infinite lease unless nailed in dhcp-host line. */
608 lease_set_expires(lease,
609 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
610 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000611 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100612
Simon Kelley7622fc02009-06-04 20:32:05 +0100613 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000614 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +0100615 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100616 }
617 }
618
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100619 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000620
Simon Kelley7de060b2011-08-26 17:24:52 +0100621 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100622 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000623
Roy Marples3f3adae2013-07-25 16:22:46 +0100624 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100625 {
626 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
627 int len = option_len(opt);
628 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100629 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100630
Simon Kelley9fed0f72012-08-30 11:43:35 +0100631 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100632 len -= 3;
633 op += 3;
634 pp = op;
635
Simon Kelley9fed0f72012-08-30 11:43:35 +0100636 /* NB, the following always sets at least one bit */
637 if (option_bool(OPT_FQDN_UPDATE))
638 {
639 if (fqdn_flags & 0x01)
640 {
641 fqdn_flags |= 0x02; /* set O */
642 fqdn_flags &= ~0x01; /* clear S */
643 }
644 fqdn_flags |= 0x08; /* set N */
645 }
646 else
647 {
648 if (!(fqdn_flags & 0x01))
649 fqdn_flags |= 0x03; /* set S and O */
650 fqdn_flags &= ~0x08; /* clear N */
651 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100652
653 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100654 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100655 {
656 memcpy(pq, op+1, *op);
657 pq += *op;
658 op += (*op)+1;
659 *(pq++) = '.';
660 }
661 else
662 {
663 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000664 if (len > 0 && op[len-1] == 0)
665 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100666 pq += len + 1;
667 }
668
669 if (pq != daemon->dhcp_buff)
670 pq--;
671
672 *pq = 0;
673
Simon Kelley1f15b812009-10-13 17:49:32 +0100674 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100675 offer_hostname = client_hostname = daemon->dhcp_buff;
676 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000677 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000678 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000679 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100680 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000681 /* Microsoft clients are broken, and need zero-terminated strings
682 in options. We detect this state here, and do the same in
683 any options we send */
684 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
685 borken_opt = 1;
686 else
687 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100688 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100689 client_hostname = daemon->dhcp_buff;
690 }
691
Simon Kelley28866e92011-02-14 20:19:14 +0000692 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100693 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
694
Simon Kelley3d8df262005-08-29 12:19:27 +0100695 if (have_config(config, CONFIG_NAME))
696 {
697 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000698 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000699 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000700 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100701 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000702 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100703 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100704 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100705 {
Simon Kelley9009d742008-11-14 20:04:27 +0000706 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100707
708 if (strlen(client_hostname) != 0)
709 {
710 hostname = client_hostname;
711 if (!config)
712 {
713 /* Search again now we have a hostname.
714 Only accept configs without CLID and HWADDR here, (they won't match)
715 to avoid impersonation by name. */
716 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
717 mess->chaddr, mess->hlen,
718 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000719 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000720 {
721 config = new;
722 /* set "known" tag for known hosts */
723 known_id.net = "known";
724 known_id.next = netid;
725 netid = &known_id;
726 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100727 }
728 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000729 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100730
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100731 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100732 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100733 struct dhcp_netid_list *list;
734
735 for (list = config->netid; list; list = list->next)
736 {
737 list->list->next = netid;
738 netid = list->list;
739 }
Simon Kelleya2226412004-05-13 20:27:08 +0100740 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100741
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100742 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100743
Simon Kelley26128d22004-11-14 16:43:54 +0000744 /* if all the netids in the ignore list are present, ignore this client */
745 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100746 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000747 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100748
749 /* If configured, we can override the server-id to be the address of the relay,
750 so that all traffic goes via the relay and can pick up agent-id info. This can be
751 configured for all relays, or by address. */
752 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
753 {
754 if (!daemon->override_relays)
755 override = mess->giaddr;
756 else
757 {
758 struct addr_list *l;
759 for (l = daemon->override_relays; l; l = l->next)
760 if (l->addr.s_addr == mess->giaddr.s_addr)
761 break;
762 if (l)
763 override = mess->giaddr;
764 }
765 }
766
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100767 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
768 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000769 clid = NULL;
770
Simon Kelley7622fc02009-06-04 20:32:05 +0100771 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100772 if (daemon->enable_pxe &&
773 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100774 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
775 {
776 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
777 {
778 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
779 uuid = pxe_uuid;
780 }
781
782 /* Check if this is really a PXE bootserver request, and handle specially if so. */
783 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
784 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
785 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
786 {
787 struct pxe_service *service;
788 int type = option_uint(opt, 0, 2);
789 int layer = option_uint(opt, 2, 2);
790 unsigned char save71[4];
791 struct dhcp_opt opt71;
792
Simon Kelley1f15b812009-10-13 17:49:32 +0100793 if (ignore)
794 return 0;
795
Simon Kelley7622fc02009-06-04 20:32:05 +0100796 if (layer & 0x8000)
797 {
798 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
799 return 0;
800 }
801
802 memcpy(save71, option_ptr(opt, 0), 4);
803
804 for (service = daemon->pxe_services; service; service = service->next)
805 if (service->type == type)
806 break;
807
Simon Kelley549b1a42015-05-20 20:20:24 +0100808 for (; context; context = context->current)
809 if (match_netid(context->filter, tagif_netid, 1) &&
810 is_same_net(mess->ciaddr, context->start, context->netmask))
811 break;
Simon Kelley7622fc02009-06-04 20:32:05 +0100812
Simon Kelley549b1a42015-05-20 20:20:24 +0100813 if (!service || !service->basename || !context)
814 return 0;
815
Simon Kelley7622fc02009-06-04 20:32:05 +0100816 clear_packet(mess, end);
817
818 mess->yiaddr = mess->ciaddr;
819 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000820 if (service->sname)
821 mess->siaddr = a_record_from_hosts(service->sname, now);
822 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100823 mess->siaddr = service->server;
824 else
825 mess->siaddr = context->local;
826
827 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
828 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
829 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
830 pxe_misc(mess, end, uuid);
831
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100832 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 opt71.val = save71;
834 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
835 opt71.len = 4;
836 opt71.flags = DHOPT_VENDOR_MATCH;
837 opt71.netid = NULL;
838 opt71.next = daemon->dhcp_opts;
839 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
840
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100841 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000842 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100843 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100844 }
845
846 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
847 {
848 pxearch = option_uint(opt, 0, 2);
849
Simon Kelley316e2732010-01-22 20:16:09 +0000850 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000851 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100852 {
Simon Kelley28866e92011-02-14 20:19:14 +0000853 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100854
Simon Kelley28866e92011-02-14 20:19:14 +0000855 for (tmp = context; tmp; tmp = tmp->current)
856 if ((tmp->flags & CONTEXT_PROXY) &&
857 match_netid(tmp->filter, tagif_netid, 1))
858 break;
859
860 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100861 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100862 struct dhcp_boot *boot;
863
864 if (tmp->netid.net)
865 {
866 tmp->netid.next = netid;
867 tagif_netid = run_tag_if(&tmp->netid);
868 }
869
870 boot = find_boot(tagif_netid);
871
Simon Kelley28866e92011-02-14 20:19:14 +0000872 mess->yiaddr.s_addr = 0;
873 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
874 {
875 mess->ciaddr.s_addr = 0;
876 mess->flags |= htons(0x8000); /* broadcast */
877 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100878
Simon Kelley28866e92011-02-14 20:19:14 +0000879 clear_packet(mess, end);
880
881 /* Provide the bootfile here, for gPXE, and in case we have no menu items
882 and set discovery_control = 8 */
883 if (boot)
884 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100885 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000886 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100887 else if (boot->tftp_sname)
888 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000889
890 if (boot->file)
891 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
892 }
893
894 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
895 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
Simon Kelley62018e12015-05-14 21:30:00 +0100896 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
Simon Kelley28866e92011-02-14 20:19:14 +0000897 pxe_misc(mess, end, uuid);
898 prune_vendor_opts(tagif_netid);
Simon Kelley62018e12015-05-14 21:30:00 +0100899 do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
Simon Kelley28866e92011-02-14 20:19:14 +0000900
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100901 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000902 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100903 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100904 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100905 }
906 }
907 }
908
909 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000910 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100911 return 0;
912
Simon Kelleybb01cb92004-12-13 20:56:23 +0000913 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100914 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100915 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100916 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000917 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100918 }
919
Simon Kelley3be34542004-09-11 19:12:13 +0100920 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000921 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000922 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000923 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000924 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000925 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100926
Simon Kelley44a2a312004-03-10 20:04:35 +0000927 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100928 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000929
Simon Kelleybb01cb92004-12-13 20:56:23 +0000930 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000931 return 0;
932
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100933 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 +0000934
935 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
936 lease_prune(lease, now);
937
Simon Kelley33820b72004-04-03 21:10:00 +0100938 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000939 config->addr.s_addr == option_addr(opt).s_addr)
940 {
Simon Kelley849a8352006-06-09 21:02:31 +0100941 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100942 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100943 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100944 config->flags |= CONFIG_DECLINED;
945 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000946 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100947 else
948 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100949 for (; context; context = context->current)
950 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000951
952 return 0;
953
954 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100955 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100956 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000957 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000958 return 0;
959
Simon Kelley44a2a312004-03-10 20:04:35 +0000960 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
961 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100962 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000963 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100964
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100965 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000966
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000967 return 0;
968
969 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100970 if (ignore || have_config(config, CONFIG_DISABLE))
971 {
Simon Kelleycc1a29e2014-03-20 15:47:18 +0000972 if (option_bool(OPT_QUIET_DHCP))
973 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000974 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100975 opt = NULL;
976 }
977 else
978 {
979 struct in_addr addr, conf;
980
Simon Kelley1a6bca82008-07-11 11:11:42 +0100981 addr.s_addr = conf.s_addr = 0;
982
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100983 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
984 addr = option_addr(opt);
985
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100986 if (have_config(config, CONFIG_ADDR))
987 {
Simon Kelley849a8352006-06-09 21:02:31 +0100988 char *addrs = inet_ntoa(config->addr);
989
Simon Kelley9009d742008-11-14 20:04:27 +0000990 if ((ltmp = lease_find_by_addr(config->addr)) &&
991 ltmp != lease &&
992 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000993 {
994 int len;
995 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
996 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100997 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100998 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000999 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001000 else
1001 {
1002 struct dhcp_context *tmp;
1003 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001004 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001005 break;
1006 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +01001007 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
Simon Kelley849a8352006-06-09 21:02:31 +01001008 else if (have_config(config, CONFIG_DECLINED) &&
1009 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +01001010 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001011 else
1012 conf = config->addr;
1013 }
1014 }
1015
1016 if (conf.s_addr)
1017 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001018 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001019 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001020 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001021 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001022 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001023 !config_find_by_address(daemon->dhcp_conf, addr))
1024 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001025 else if (emac_len == 0)
1026 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001027 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001028 message = _("no address available");
1029 }
1030
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001031 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 +01001032
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001033 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001034 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001035
Simon Kelleycdeda282006-03-16 20:16:06 +00001036 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001037 {
1038 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001039 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001040 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001041
Simon Kelley4cb1b322012-02-06 14:30:41 +00001042 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001043
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001044 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001045
Simon Kelley824af852008-02-12 20:43:05 +00001046 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001047 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001048 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001049 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001050 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001051 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley9009d742008-11-14 20:04:27 +00001052 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001053 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001054
Simon Kelley7de060b2011-08-26 17:24:52 +01001055 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001056
1057 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001058 if (ignore || have_config(config, CONFIG_DISABLE))
1059 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001060 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001061 {
1062 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001063 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001064
Simon Kelley4011c4e2006-10-28 16:26:19 +01001065 /* send vendor and user class info for new or recreated lease */
1066 do_classes = 1;
1067
Simon Kelleybb01cb92004-12-13 20:56:23 +00001068 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001069 {
Simon Kelley3be34542004-09-11 19:12:13 +01001070 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001071 selecting = 1;
1072
Simon Kelley1a6bca82008-07-11 11:11:42 +01001073 if (override.s_addr != 0)
1074 {
1075 if (option_addr(opt).s_addr != override.s_addr)
1076 return 0;
1077 }
Simon Kelley9009d742008-11-14 20:04:27 +00001078 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001079 {
1080 for (; context; context = context->current)
1081 if (context->local.s_addr == option_addr(opt).s_addr)
1082 break;
1083
1084 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001085 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001086 /* Handle very strange configs where clients have more than one route to the server.
1087 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1088 Have to set override to make sure we echo back the correct server-id */
1089 struct irec *intr;
1090
Simon Kelley115ac3e2013-05-20 11:28:32 +01001091 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001092
1093 for (intr = daemon->interfaces; intr; intr = intr->next)
1094 if (intr->addr.sa.sa_family == AF_INET &&
1095 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1096 intr->tftp_ok)
1097 break;
1098
1099 if (intr)
1100 override = intr->addr.in.sin_addr;
1101 else
1102 {
1103 /* In auth mode, a REQUEST sent to the wrong server
1104 should be faulted, so that the client establishes
1105 communication with us, otherwise, silently ignore. */
1106 if (!option_bool(OPT_AUTHORITATIVE))
1107 return 0;
1108 message = _("wrong server-ID");
1109 }
Simon Kelley9009d742008-11-14 20:04:27 +00001110 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001111 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001112
Simon Kelley3be34542004-09-11 19:12:13 +01001113 /* If a lease exists for this host and another address, squash it. */
1114 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1115 {
1116 lease_prune(lease, now);
1117 lease = NULL;
1118 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001119 }
Simon Kelley3be34542004-09-11 19:12:13 +01001120 else
1121 {
1122 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001123 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001124 return 0;
1125
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001126 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001127 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001128 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001129 }
1130 else
1131 {
1132 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001133 /* Check existing lease for this address.
1134 We allow it to be missing if dhcp-authoritative mode
1135 as long as we can allocate the lease now - checked below.
1136 This makes for a smooth recovery from a lost lease DB */
1137 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001138 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001139 {
Simon Kelley28866e92011-02-14 20:19:14 +00001140 /* A client rebinding will broadcast the request, so we may see it even
1141 if the lease is held by another server. Just ignore it in that case.
1142 If the request is unicast to us, then somethings wrong, NAK */
1143 if (!unicast_dest)
1144 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001145 message = _("lease not found");
1146 /* ensure we broadcast NAK */
1147 unicast_dest = 0;
1148 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001149
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001150 /* desynchronise renewals */
1151 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001152 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001153 }
1154
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001155 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001156
Simon Kelleydfa666f2004-08-02 18:27:27 +01001157 if (!message)
1158 {
1159 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001160 struct dhcp_context *tmp = NULL;
1161
1162 if (have_config(config, CONFIG_ADDR))
1163 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001164 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001165 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001166
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001167 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001168 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001169 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001170 message = _("wrong network");
1171 /* ensure we broadcast NAK */
1172 unicast_dest = 0;
1173 }
1174
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001175 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001176 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001177 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001178 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001179
Simon Kelleydfa666f2004-08-02 18:27:27 +01001180 /* Check if a new static address has been configured. Be very sure that
1181 when the client does DISCOVER, it will get the static address, otherwise
1182 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001183 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001184 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001185 (!have_config(config, CONFIG_DECLINED) ||
1186 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001187 config->addr.s_addr != mess->yiaddr.s_addr &&
1188 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001189 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001190
1191 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001192 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001193 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001194
Simon Kelley9009d742008-11-14 20:04:27 +00001195 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1196 {
1197 /* If a host is configured with more than one MAC address, it's OK to 'nix
1198 a lease from one of it's MACs to give the address to another. */
1199 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1200 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001201 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001202 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1203 inet_ntoa(ltmp->addr));
1204 lease = ltmp;
1205 }
Simon Kelley16972692006-10-16 20:04:18 +01001206 else
Simon Kelley9009d742008-11-14 20:04:27 +00001207 message = _("address in use");
1208 }
1209
1210 if (!message)
1211 {
1212 if (emac_len == 0)
1213 message = _("no unique-id");
1214
1215 else if (!lease)
1216 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001217 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001218 do_classes = 1;
1219 else
1220 message = _("no leases left");
1221 }
Simon Kelley16972692006-10-16 20:04:18 +01001222 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001223 }
Simon Kelley16972692006-10-16 20:04:18 +01001224
Simon Kelley44a2a312004-03-10 20:04:35 +00001225 if (message)
1226 {
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001227 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001228
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001229 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001230 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001231 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001232 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001233 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001234 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1235 a distant subnet which unicast a REQ to us won't work. */
1236 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1237 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1238 {
1239 mess->flags |= htons(0x8000); /* broadcast */
1240 mess->ciaddr.s_addr = 0;
1241 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001242 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001243 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001244 {
Simon Kelley316e2732010-01-22 20:16:09 +00001245 if (context->netid.net)
1246 {
1247 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001248 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001249 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001250
Simon Kelley4cb1b322012-02-06 14:30:41 +00001251 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001252
Simon Kelleydcffad22012-04-24 15:25:18 +01001253 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001254 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001255 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001256 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001257
Simon Kelleydcffad22012-04-24 15:25:18 +01001258#ifdef HAVE_SCRIPT
1259 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001260 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001261 struct dhcp_netid *n;
1262
1263 if (mess->giaddr.s_addr)
1264 lease->giaddr = mess->giaddr;
1265
1266 free(lease->extradata);
1267 lease->extradata = NULL;
1268 lease->extradata_size = lease->extradata_len = 0;
1269
1270 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1271 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1272 add_extradata_opt(lease, oui);
1273 add_extradata_opt(lease, serial);
1274 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001275
1276 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1277 {
1278 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1279 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1280 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1281 }
1282 else
1283 {
1284 add_extradata_opt(lease, NULL);
1285 add_extradata_opt(lease, NULL);
1286 add_extradata_opt(lease, NULL);
1287 }
1288
Simon Kelleydcffad22012-04-24 15:25:18 +01001289 /* space-concat tag set */
1290 if (!tagif_netid)
1291 add_extradata_opt(lease, NULL);
1292 else
1293 for (n = tagif_netid; n; n = n->next)
1294 {
1295 struct dhcp_netid *n1;
1296 /* kill dupes */
1297 for (n1 = n->next; n1; n1 = n1->next)
1298 if (strcmp(n->net, n1->net) == 0)
1299 break;
1300 if (!n1)
1301 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1302 }
1303
1304 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1305 {
1306 int len = option_len(opt);
1307 unsigned char *ucp = option_ptr(opt, 0);
1308 /* If the user-class option started as counted strings, the first byte will be zero. */
1309 if (len != 0 && ucp[0] == 0)
1310 ucp++, len--;
1311 lease_add_extradata(lease, ucp, len, 0);
1312 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001313 }
Simon Kelley316e2732010-01-22 20:16:09 +00001314#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001315 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001316
1317 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1318 {
1319 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001320 hostname = client_hostname;
1321 hostname_auth = 1;
1322 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001323
Simon Kelley824af852008-02-12 20:43:05 +00001324 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001325 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001326
Simon Kelley832af0b2007-01-21 20:01:28 +00001327 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1328 if (!hostname_auth)
1329 {
1330 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001331 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001332 break;
1333 if (id_list)
1334 hostname = NULL;
1335 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001336
1337 /* Last ditch, if configured, generate hostname from mac address */
1338 if (!hostname && emac_len != 0)
1339 {
1340 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1341 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1342 break;
1343 if (id_list)
1344 {
1345 int i;
1346
1347 hostname = daemon->dhcp_buff;
1348 /* buffer is 256 bytes, 3 bytes per octet */
1349 for (i = 0; (i < emac_len) && (i < 80); i++)
1350 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1351 hostname = daemon->dhcp_buff;
1352 }
1353 }
1354
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001355 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001356 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001357
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001358 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001359 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001360
1361 if (override.s_addr != 0)
1362 lease->override = override;
1363 else
1364 override = lease->override;
1365
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001366 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001367
Simon Kelley7622fc02009-06-04 20:32:05 +01001368 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001369 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
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(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley9009d742008-11-14 20:04:27 +00001372 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001373 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
Simon Kelley44a2a312004-03-10 20:04:35 +00001374 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001375
Simon Kelley7de060b2011-08-26 17:24:52 +01001376 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001377
1378 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001379 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001380 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001381
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001382 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001383
Simon Kelley73a08a22009-02-05 20:28:08 +00001384 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001385 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001386
1387 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001388 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001389
Simon Kelley5aabfc72007-08-29 11:24:47 +01001390 /* Find a least based on IP address if we didn't
1391 get one from MAC address/client-d */
1392 if (!lease &&
1393 (lease = lease_find_by_addr(mess->ciaddr)) &&
1394 lease->hostname)
1395 hostname = lease->hostname;
1396
Simon Kelley0f371f92013-07-27 15:15:38 +01001397 if (!hostname)
1398 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001399
Simon Kelley73a08a22009-02-05 20:28:08 +00001400 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001401 {
1402 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001403 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001404 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001405
Simon Kelley4cb1b322012-02-06 14:30:41 +00001406 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001407
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001408 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001409
Simon Kelley3927da42008-07-20 15:10:39 +01001410 if (lease)
1411 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001412 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001413 if (override.s_addr != 0)
1414 lease->override = override;
1415 else
1416 override = lease->override;
1417 }
1418
Simon Kelley7622fc02009-06-04 20:32:05 +01001419 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001420 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001421 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001422
1423 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1424 we supply a utility which makes DHCPINFORM requests to get this information.
1425 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1426 which won't be true for ordinary clients, but will be true for the
1427 dhcp_lease_time utility. */
1428 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1429 {
1430 if (lease->expires == 0)
1431 time = 0xffffffff;
1432 else
1433 time = (unsigned int)difftime(lease->expires, now);
1434 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1435 }
1436
Simon Kelley9009d742008-11-14 20:04:27 +00001437 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelleyca85a282015-05-13 22:33:04 +01001438 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001439
Simon Kelley5aabfc72007-08-29 11:24:47 +01001440 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001441 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001442 }
1443
1444 return 0;
1445}
1446
Simon Kelley6b010842007-02-12 20:32:07 +00001447/* find a good value to use as MAC address for logging and address-allocation hashing.
1448 This is normally just the chaddr field from the DHCP packet,
1449 but eg Firewire will have hlen == 0 and use the client-id instead.
1450 This could be anything, but will normally be EUI64 for Firewire.
1451 We assume that if the first byte of the client-id equals the htype byte
1452 then the client-id is using the usual encoding and use the rest of the
1453 client-id: if not we can use the whole client-id. This should give
1454 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001455unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001456 int clid_len, unsigned char *clid, int *len_out)
1457{
1458 if (hwlen == 0 && clid && clid_len > 3)
1459 {
1460 if (clid[0] == hwtype)
1461 {
1462 *len_out = clid_len - 1 ;
1463 return clid + 1;
1464 }
1465
1466#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1467 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1468 {
1469 *len_out = clid_len - 1 ;
1470 return clid + 1;
1471 }
1472#endif
1473
1474 *len_out = clid_len;
1475 return clid;
1476 }
1477
1478 *len_out = hwlen;
1479 return hwaddr;
1480}
1481
Simon Kelley824af852008-02-12 20:43:05 +00001482static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001483{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001484 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001485
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001486 if (opt)
1487 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001488 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001489 if (req_time < 120 )
1490 req_time = 120; /* sanity */
1491 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1492 time = req_time;
1493 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001494
1495 return time;
1496}
1497
Simon Kelley73a08a22009-02-05 20:28:08 +00001498static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001499{
Simon Kelley73a08a22009-02-05 20:28:08 +00001500 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001501 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001502 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001503 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001504 else
1505 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001506}
1507
Simon Kelleyf2621c72007-04-29 19:47:21 +01001508static int sanitise(unsigned char *opt, char *buf)
1509{
1510 char *p;
1511 int i;
1512
1513 *buf = 0;
1514
1515 if (!opt)
1516 return 0;
1517
Simon Kelley1a6bca82008-07-11 11:11:42 +01001518 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001519
1520 for (i = option_len(opt); i > 0; i--)
1521 {
1522 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001523 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001524 *buf++ = c;
1525 }
1526 *buf = 0; /* add terminator */
1527
1528 return 1;
1529}
1530
Simon Kelley316e2732010-01-22 20:16:09 +00001531#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001532static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1533{
1534 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001535 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001536 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001537 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001538}
1539#endif
1540
Simon Kelley5aabfc72007-08-29 11:24:47 +01001541static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001542 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001543{
Simon Kelley16972692006-10-16 20:04:18 +01001544 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001545
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001546 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001547 return;
1548
Simon Kelley16972692006-10-16 20:04:18 +01001549 /* addr may be misaligned */
1550 if (addr)
1551 memcpy(&a, addr, sizeof(a));
1552
Simon Kelley7622fc02009-06-04 20:32:05 +01001553 print_mac(daemon->namebuff, ext_mac, mac_len);
1554
Simon Kelley28866e92011-02-14 20:19:14 +00001555 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001556 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001557 ntohl(xid),
1558 type,
1559 interface,
1560 addr ? inet_ntoa(a) : "",
1561 addr ? " " : "",
1562 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001563 string ? string : "",
1564 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001565 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001566 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001567 type,
1568 interface,
1569 addr ? inet_ntoa(a) : "",
1570 addr ? " " : "",
1571 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001572 string ? string : "",
1573 err ? err : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001574}
1575
Simon Kelley7622fc02009-06-04 20:32:05 +01001576static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001577{
1578 while (*start != OPTION_END)
1579 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001580 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 +01001581
Simon Kelley4cb1b322012-02-06 14:30:41 +00001582 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1583 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001584 start += start[1] + 2;
1585 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001586}
1587
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001588static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001589{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001590 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001591 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001592 if (p > end)
1593 return NULL;
1594 else if (*p == OPTION_END)
1595 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001596 else if (*p == OPTION_PAD)
1597 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001598 else
1599 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001600 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001601 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001602 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001603 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001604 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001605 return NULL; /* malformed packet */
1606 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001607 return p;
1608 p += opt_len + 2;
1609 }
1610 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001611}
1612
Simon Kelleycdeda282006-03-16 20:16:06 +00001613static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001614{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001615 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001616
Simon Kelley3be34542004-09-11 19:12:13 +01001617 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001618 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1619 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001620
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001621 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001622 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001623 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001624
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001625 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001626 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001627 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1628 return ret;
1629
1630 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001631 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001632 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1633 return ret;
1634
1635 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001636}
1637
Simon Kelley4cb1b322012-02-06 14:30:41 +00001638static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001639{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001640 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001641 /* struct in_addr is network byte order */
1642 struct in_addr ret;
1643
Simon Kelley4cb1b322012-02-06 14:30:41 +00001644 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001645
1646 return ret;
1647}
1648
Simon Kelley7622fc02009-06-04 20:32:05 +01001649static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001650{
1651 /* this worries about unaligned data and byte order */
1652 unsigned int ret = 0;
1653 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001654 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001655
1656 for (i = 0; i < size; i++)
1657 ret = (ret << 8) | *p++;
1658
1659 return ret;
1660}
1661
1662static unsigned char *dhcp_skip_opts(unsigned char *start)
1663{
1664 while (*start != 0)
1665 start += start[1] + 2;
1666 return start;
1667}
1668
1669/* only for use when building packet: doesn't check for bad data. */
1670static unsigned char *find_overload(struct dhcp_packet *mess)
1671{
1672 unsigned char *p = &mess->options[0] + sizeof(u32);
1673
1674 while (*p != 0)
1675 {
1676 if (*p == OPTION_OVERLOAD)
1677 return p;
1678 p += p[1] + 2;
1679 }
1680 return NULL;
1681}
1682
Simon Kelley7de060b2011-08-26 17:24:52 +01001683static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1684{
1685 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1686 unsigned char *overload;
1687 size_t ret;
1688
1689 /* move agent_id back down to the end of the packet */
1690 if (agent_id)
1691 {
1692 memmove(p, agent_id, real_end - agent_id);
1693 p += real_end - agent_id;
1694 memset(p, 0, real_end - p); /* in case of overlap */
1695 }
1696
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001697 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001698 overload = find_overload(mess);
1699
1700 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001701 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001702 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001703 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001704 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001705 }
Simon Kelley28866e92011-02-14 20:19:14 +00001706 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001707 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1708
1709 if (overload && (option_uint(overload, 0, 1) & 2))
1710 {
1711 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001712 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001713 log_options(mess->sname, mess->xid);
1714 }
Simon Kelley28866e92011-02-14 20:19:14 +00001715 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001716 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1717
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001718
1719 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001720
Simon Kelley28866e92011-02-14 20:19:14 +00001721 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001722 {
1723 if (mess->siaddr.s_addr != 0)
1724 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1725
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001726 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1727 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1728
Simon Kelley7622fc02009-06-04 20:32:05 +01001729 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1730 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001731
1732 ret = (size_t)(p - (unsigned char *)mess);
1733
1734 if (ret < MIN_PACKETSZ)
1735 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001736
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001737 return ret;
1738}
1739
1740static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1741{
1742 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1743
1744 if (p + len + 3 >= end)
1745 /* not enough space in options area, try and use overload, if poss */
1746 {
1747 unsigned char *overload;
1748
1749 if (!(overload = find_overload(mess)) &&
1750 (mess->file[0] == 0 || mess->sname[0] == 0))
1751 {
1752 /* attempt to overload fname and sname areas, we've reserved space for the
1753 overflow option previuously. */
1754 overload = p;
1755 *(p++) = OPTION_OVERLOAD;
1756 *(p++) = 1;
1757 }
1758
1759 p = NULL;
1760
1761 /* using filename field ? */
1762 if (overload)
1763 {
1764 if (mess->file[0] == 0)
1765 overload[2] |= 1;
1766
1767 if (overload[2] & 1)
1768 {
1769 p = dhcp_skip_opts(mess->file);
1770 if (p + len + 3 >= mess->file + sizeof(mess->file))
1771 p = NULL;
1772 }
1773
1774 if (!p)
1775 {
1776 /* try to bring sname into play (it may be already) */
1777 if (mess->sname[0] == 0)
1778 overload[2] |= 2;
1779
1780 if (overload[2] & 2)
1781 {
1782 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001783 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001784 p = NULL;
1785 }
1786 }
1787 }
1788
1789 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001790 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 +00001791 }
1792
1793 if (p)
1794 {
1795 *(p++) = opt;
1796 *(p++) = len;
1797 }
1798
1799 return p;
1800}
1801
1802static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1803{
1804 int i;
1805 unsigned char *p = free_space(mess, end, opt, len);
1806
1807 if (p)
1808 for (i = 0; i < len; i++)
1809 *(p++) = val >> (8 * (len - (i + 1)));
1810}
1811
1812static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1813 char *string, int null_term)
1814{
1815 unsigned char *p;
1816 size_t len = strlen(string);
1817
1818 if (null_term && len != 255)
1819 len++;
1820
1821 if ((p = free_space(mess, end, opt, len)))
1822 memcpy(p, string, len);
1823}
1824
1825/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001826static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001827{
1828 int len = opt->len;
1829
1830 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1831 len++;
1832
1833 if (p && len != 0)
1834 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001835 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001836 {
1837 int j;
1838 struct in_addr *a = (struct in_addr *)opt->val;
1839 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1840 {
1841 /* zero means "self" (but not in vendorclass options.) */
1842 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001843 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001844 else
1845 memcpy(p, a, INADDRSZ);
1846 p += INADDRSZ;
1847 }
1848 }
1849 else
Simon Kelley625ac282013-07-02 21:19:32 +01001850 /* empty string may be extended to "\0" by null_term */
1851 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001852 }
1853 return len;
1854}
Simon Kelley7622fc02009-06-04 20:32:05 +01001855
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001856static int in_list(unsigned char *list, int opt)
1857{
1858 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001859
1860 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001861 if (!list)
1862 return 1;
1863
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001864 for (i = 0; list[i] != OPTION_END; i++)
1865 if (opt == list[i])
1866 return 1;
1867
1868 return 0;
1869}
1870
Simon Kelley7de060b2011-08-26 17:24:52 +01001871static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001872{
Simon Kelley7de060b2011-08-26 17:24:52 +01001873 struct dhcp_opt *opts;
1874
1875 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1876 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1877 return opts;
1878
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001879 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001880}
1881
Simon Kelley6b010842007-02-12 20:32:07 +00001882/* mark vendor-encapsulated options which match the client-supplied or
1883 config-supplied vendor class */
1884static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1885{
1886 for (; dopt; dopt = dopt->next)
1887 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001888 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001889 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001890 {
1891 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001892 if (dopt->u.vendor_class)
1893 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001894 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001895 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001896 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001897 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001898 break;
1899 }
1900 }
1901 }
1902}
1903
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001904static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1905 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001906{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001907 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001908 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001909 unsigned char *p;
1910
1911 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001912 for (enc_len = 0, start = opt; opt; opt = opt->next)
1913 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001914 {
1915 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001916 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001917 if (enc_len + new <= 255)
1918 enc_len += new;
1919 else
1920 {
1921 p = free_space(mess, end, encap, enc_len);
1922 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001923 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001924 {
1925 len = do_opt(start, p + 2, NULL, null_term);
1926 *(p++) = start->opt;
1927 *(p++) = len;
1928 p += len;
1929 }
1930 enc_len = new;
1931 start = opt;
1932 }
1933 }
1934
1935 if (enc_len != 0 &&
1936 (p = free_space(mess, end, encap, enc_len + 1)))
1937 {
1938 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001939 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001940 {
1941 len = do_opt(start, p + 2, NULL, null_term);
1942 *(p++) = start->opt;
1943 *(p++) = len;
1944 p += len;
1945 }
1946 *p = OPTION_END;
1947 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001948
1949 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00001950}
1951
Simon Kelley7622fc02009-06-04 20:32:05 +01001952static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01001953{
Simon Kelley7622fc02009-06-04 20:32:05 +01001954 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01001955
Simon Kelley7622fc02009-06-04 20:32:05 +01001956 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1957 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1958 memcpy(p, uuid, 17);
1959}
1960
1961static int prune_vendor_opts(struct dhcp_netid *netid)
1962{
1963 int force = 0;
1964 struct dhcp_opt *opt;
1965
1966 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1967 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1968 if (opt->flags & DHOPT_VENDOR_MATCH)
1969 {
1970 if (!match_netid(opt->netid, netid, 1))
1971 opt->flags &= ~DHOPT_VENDOR_MATCH;
1972 else if (opt->flags & DHOPT_FORCE)
1973 force = 1;
1974 }
1975 return force;
1976}
1977
Simon Kelley751d6f42012-02-10 15:24:51 +00001978static 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 +01001979{
1980#define NUM_OPTS 4
1981
1982 unsigned char *p, *q;
1983 struct pxe_service *service;
1984 static struct dhcp_opt *o, *ret;
1985 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001986 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01001987
1988 /* We pass back references to these, hence they are declared static */
1989 static unsigned char discovery_control;
1990 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1991 static struct dhcp_opt *fake_opts = NULL;
1992
Simon Kelley316e2732010-01-22 20:16:09 +00001993 /* Disable multicast, since we don't support it, and broadcast
1994 unless we need it */
1995 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001996
1997 ret = daemon->dhcp_opts;
1998
1999 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2000 return ret;
2001
2002 for (i = 0; i < NUM_OPTS; i++)
2003 {
2004 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2005 fake_opts[i].netid = NULL;
2006 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2007 }
2008
2009 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2010 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002011 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002012
2013 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2014 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2015 {
2016 size_t len = strlen(service->menu);
2017 /* opt 43 max size is 255. encapsulated option has type and length
2018 bytes, so its max size is 253. */
2019 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2020 {
2021 *(p++) = service->type >> 8;
2022 *(p++) = service->type;
2023 *(p++) = len;
2024 memcpy(p, service->menu, len);
2025 p += len;
2026 i++;
2027 }
2028 else
2029 {
2030 toobig:
2031 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2032 return daemon->dhcp_opts;
2033 }
2034
Simon Kelley751d6f42012-02-10 15:24:51 +00002035 boot_server = service->basename ? local :
2036 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2037
Simon Kelley316e2732010-01-22 20:16:09 +00002038 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002039 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002040 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002041 goto toobig;
2042
2043 /* Boot service with known address - give it */
2044 *(q++) = service->type >> 8;
2045 *(q++) = service->type;
2046 *(q++) = 1;
2047 /* dest misaligned */
2048 memcpy(q, &boot_server.s_addr, INADDRSZ);
2049 q += INADDRSZ;
2050 }
2051 else if (service->type != 0)
2052 /* We don't know the server for a service type, so we'll
2053 allow the client to broadcast for it */
2054 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002055 }
2056
2057 /* if no prompt, wait forever if there's a choice */
2058 fake_prompt[0] = (i > 1) ? 255 : 0;
2059
2060 if (i == 0)
2061 discovery_control = 8; /* no menu - just use use mess->filename */
2062 else
2063 {
2064 ret = &fake_opts[j--];
2065 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2066 ret->val = (unsigned char *)daemon->dhcp_buff;
2067 ret->opt = SUBOPT_PXE_MENU;
2068
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002069 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002070 {
2071 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002072 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2073 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002074 ret->opt = SUBOPT_PXE_SERVERS;
2075 }
2076 }
2077
2078 for (o = daemon->dhcp_opts; o; o = o->next)
2079 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2080 break;
2081
2082 if (!o)
2083 {
2084 ret = &fake_opts[j--];
2085 ret->len = sizeof(fake_prompt);
2086 ret->val = fake_prompt;
2087 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2088 }
2089
Simon Kelley316e2732010-01-22 20:16:09 +00002090 ret = &fake_opts[j--];
2091 ret->len = 1;
2092 ret->opt = SUBOPT_PXE_DISCOVERY;
2093 ret->val= &discovery_control;
2094
Simon Kelley7622fc02009-06-04 20:32:05 +01002095 return ret;
2096}
2097
2098static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2099{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002100 memset(mess->sname, 0, sizeof(mess->sname));
2101 memset(mess->file, 0, sizeof(mess->file));
2102 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2103 mess->siaddr.s_addr = 0;
2104}
Simon Kelleycdeda282006-03-16 20:16:06 +00002105
Simon Kelley7622fc02009-06-04 20:32:05 +01002106struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002107{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002108 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002109
2110 /* decide which dhcp-boot option we're using */
2111 for (boot = daemon->boot_config; boot; boot = boot->next)
2112 if (match_netid(boot->netid, netid, 0))
2113 break;
2114 if (!boot)
2115 /* No match, look for one without a netid */
2116 for (boot = daemon->boot_config; boot; boot = boot->next)
2117 if (match_netid(boot->netid, netid, 1))
2118 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002119
2120 return boot;
2121}
2122
2123static void do_options(struct dhcp_context *context,
2124 struct dhcp_packet *mess,
2125 unsigned char *end,
2126 unsigned char *req_options,
2127 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002128 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002129 struct dhcp_netid *netid,
2130 struct in_addr subnet_addr,
2131 unsigned char fqdn_flags,
2132 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002133 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002134 int vendor_class_len,
Simon Kelleyca85a282015-05-13 22:33:04 +01002135 time_t now,
2136 unsigned int lease_time,
2137 unsigned short fuzz)
Simon Kelley7622fc02009-06-04 20:32:05 +01002138{
2139 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2140 struct dhcp_boot *boot;
2141 unsigned char *p;
2142 int i, len, force_encap = 0;
2143 unsigned char f0 = 0, s0 = 0;
2144 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002145 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002146 struct dhcp_netid *tagif;
2147 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002148
Simon Kelley4cb1b322012-02-06 14:30:41 +00002149 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002150 if (context)
2151 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002152 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002153
Simon Kelley7622fc02009-06-04 20:32:05 +01002154 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002155 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002156 {
2157 char *q = daemon->namebuff;
2158 for (i = 0; req_options[i] != OPTION_END; i++)
2159 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002160 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002161 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2162 "%d%s%s%s",
2163 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002164 strlen(s) != 0 ? ":" : "",
2165 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002166 req_options[i+1] == OPTION_END ? "" : ", ");
2167 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2168 {
2169 q = daemon->namebuff;
2170 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2171 }
2172 }
2173 }
2174
Simon Kelley7de060b2011-08-26 17:24:52 +01002175 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2176 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2177 break;
2178 if (id_list)
2179 mess->flags |= htons(0x8000); /* force broadcast */
2180
Simon Kelley73a08a22009-02-05 20:28:08 +00002181 if (context)
2182 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002183
2184 /* See if we can send the boot stuff as options.
2185 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002186 and very old DHCP clients won't have this, we also
2187 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002188 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002189 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002190 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002191 {
2192 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002193 {
Simon Kelley28866e92011-02-14 20:19:14 +00002194 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002195 req_options &&
2196 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002197 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2198 else
Simon Kelley824af852008-02-12 20:43:05 +00002199 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002200 }
2201
2202 if (boot->file)
2203 {
Simon Kelley28866e92011-02-14 20:19:14 +00002204 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002205 req_options &&
2206 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002207 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2208 else
Simon Kelley824af852008-02-12 20:43:05 +00002209 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002210 }
2211
Simon Kelley7de060b2011-08-26 17:24:52 +01002212 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002213 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002214 else if (boot->tftp_sname)
2215 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002216 }
Simon Kelley824af852008-02-12 20:43:05 +00002217 else
2218 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002219 they're not explicitly asked for as options. OPTION_END is used
2220 as an internal way to specify siaddr without using dhcp-boot, for use in
2221 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002222 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002223 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002224 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002225 {
2226 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2227 done_file = 1;
2228 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002229
Simon Kelley824af852008-02-12 20:43:05 +00002230 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002231 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002232 {
2233 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2234 done_server = 1;
2235 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002236
Simon Kelley7de060b2011-08-26 17:24:52 +01002237 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002238 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002239 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002240
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002241 /* We don't want to do option-overload for BOOTP, so make the file and sname
2242 fields look like they are in use, even when they aren't. This gets restored
2243 at the end of this function. */
2244
Simon Kelley28866e92011-02-14 20:19:14 +00002245 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002246 {
2247 f0 = mess->file[0];
2248 mess->file[0] = 1;
2249 s0 = mess->sname[0];
2250 mess->sname[0] = 1;
2251 }
2252
2253 /* At this point, if mess->sname or mess->file are zeroed, they are available
2254 for option overload, reserve space for the overload option. */
2255 if (mess->file[0] == 0 || mess->sname[0] == 0)
2256 end -= 3;
2257
Simon Kelley3be34542004-09-11 19:12:13 +01002258 /* rfc3011 says this doesn't need to be in the requested options list. */
2259 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002260 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelleyca85a282015-05-13 22:33:04 +01002261
2262 if (lease_time != 0xffffffff)
2263 {
2264 unsigned int t1val = lease_time/2;
2265 unsigned int t2val = (lease_time*7)/8;
2266 unsigned int hval;
2267
2268 /* If set by user, sanity check, so not longer than lease. */
2269 if ((opt = option_find2(OPTION_T1)))
2270 {
2271 hval = ntohl(*((unsigned int *)opt->val));
2272 if (hval < lease_time && hval > 2)
2273 t1val = hval;
2274 }
2275
2276 if ((opt = option_find2(OPTION_T2)))
2277 {
2278 hval = ntohl(*((unsigned int *)opt->val));
2279 if (hval < lease_time && hval > 2)
2280 t2val = hval;
2281 }
2282
Simon Kelley7c0f2542015-05-14 21:16:18 +01002283 /* ensure T1 is still < T2 */
2284 if (t2val <= t1val)
2285 t1val = t2val - 1;
2286
Simon Kelleyca85a282015-05-13 22:33:04 +01002287 while (fuzz > (t1val/8))
2288 fuzz = fuzz/2;
2289
2290 t1val -= fuzz;
2291 t2val -= fuzz;
2292
Simon Kelleyca85a282015-05-13 22:33:04 +01002293 option_put(mess, end, OPTION_T1, 4, t1val);
2294 option_put(mess, end, OPTION_T2, 4, t2val);
2295 }
2296
Simon Kelley73a08a22009-02-05 20:28:08 +00002297 /* replies to DHCPINFORM may not have a valid context */
2298 if (context)
2299 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002300 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002301 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2302
2303 /* May not have a "guessed" broadcast address if we got no packets via a relay
2304 from this net yet (ie just unicast renewals after a restart */
2305 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002306 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002307 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2308
2309 /* Same comments as broadcast apply, and also may not be able to get a sensible
2310 default when using subnet select. User must configure by steam in that case. */
2311 if (context->router.s_addr &&
2312 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002313 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002314 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2315
Simon Kelleya21e27b2013-02-17 16:41:35 +00002316 if (daemon->port == NAMESERVER_PORT &&
2317 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002318 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002319 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2320 }
Simon Kelley3be34542004-09-11 19:12:13 +01002321
Simon Kelley9009d742008-11-14 20:04:27 +00002322 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002323 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002324 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002325
Simon Kelley824af852008-02-12 20:43:05 +00002326 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002327 if (hostname)
2328 {
Simon Kelley824af852008-02-12 20:43:05 +00002329 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002330 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002331 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002332
2333 if (fqdn_flags != 0)
2334 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002335 len = strlen(hostname) + 3;
2336
Simon Kelley3d8df262005-08-29 12:19:27 +01002337 if (fqdn_flags & 0x04)
2338 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002339 else if (null_term)
2340 len++;
2341
Simon Kelley9009d742008-11-14 20:04:27 +00002342 if (domain)
2343 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002344 else if (fqdn_flags & 0x04)
2345 len--;
2346
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002347 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002348 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002349 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002350 *(p++) = 255;
2351 *(p++) = 255;
2352
2353 if (fqdn_flags & 0x04)
2354 {
2355 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002356 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002357 {
2358 p = do_rfc1035_name(p, domain);
2359 *p++ = 0;
2360 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002361 }
2362 else
2363 {
2364 memcpy(p, hostname, strlen(hostname));
2365 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002366 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002367 {
2368 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002369 memcpy(p, domain, strlen(domain));
2370 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002371 }
2372 if (null_term)
2373 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002374 }
2375 }
2376 }
2377 }
2378
Simon Kelley6b010842007-02-12 20:32:07 +00002379 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002380 {
Simon Kelley824af852008-02-12 20:43:05 +00002381 int optno = opt->opt;
2382
Simon Kelley7de060b2011-08-26 17:24:52 +01002383 /* netids match and not encapsulated? */
2384 if (!(opt->flags & DHOPT_TAGOK))
2385 continue;
2386
Simon Kelley6b010842007-02-12 20:32:07 +00002387 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002388 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002389 continue;
2390
Simon Kelleyca85a282015-05-13 22:33:04 +01002391 /* prohibit some used-internally options. T1 and T2 already handled. */
Simon Kelley824af852008-02-12 20:43:05 +00002392 if (optno == OPTION_CLIENT_FQDN ||
2393 optno == OPTION_MAXMESSAGE ||
2394 optno == OPTION_OVERLOAD ||
2395 optno == OPTION_PAD ||
Simon Kelleyca85a282015-05-13 22:33:04 +01002396 optno == OPTION_END ||
2397 optno == OPTION_T1 ||
2398 optno == OPTION_T2)
Simon Kelley824af852008-02-12 20:43:05 +00002399 continue;
2400
2401 if (optno == OPTION_SNAME && done_server)
2402 continue;
2403
2404 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002405 continue;
2406
Simon Kelley33820b72004-04-03 21:10:00 +01002407 /* For the options we have default values on
2408 dhc-option=<optionno> means "don't include this option"
2409 not "include a zero-length option" */
2410 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002411 (optno == OPTION_NETMASK ||
2412 optno == OPTION_BROADCAST ||
2413 optno == OPTION_ROUTER ||
2414 optno == OPTION_DNSSERVER ||
2415 optno == OPTION_DOMAINNAME ||
2416 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002417 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002418
2419 /* vendor-class comes from elsewhere for PXE */
2420 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2421 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002422
Simon Kelley7622fc02009-06-04 20:32:05 +01002423 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002424 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002425 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002426
Simon Kelley824af852008-02-12 20:43:05 +00002427 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002428 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002429 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002430 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2431
Simon Kelley6b010842007-02-12 20:32:07 +00002432 /* If we send a vendor-id, revisit which vendor-ops we consider
2433 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002434 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002435 {
2436 match_vendor_opts(p - 2, config_opts);
2437 done_vendor_class = 1;
2438 }
Simon Kelley6b010842007-02-12 20:32:07 +00002439 }
2440 }
2441
Simon Kelley73a08a22009-02-05 20:28:08 +00002442 /* Now send options to be encapsulated in arbitrary options,
2443 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002444 Also handle vendor-identifying vendor-encapsulated options,
2445 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002446 The may be more that one "outer" to do, so group
2447 all the options which match each outer in turn. */
2448 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002449 opt->flags &= ~DHOPT_ENCAP_DONE;
2450
2451 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002452 {
2453 int flags;
2454
2455 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2456 {
2457 int found = 0;
2458 struct dhcp_opt *o;
2459
2460 if (opt->flags & DHOPT_ENCAP_DONE)
2461 continue;
2462
2463 for (len = 0, o = config_opts; o; o = o->next)
2464 {
2465 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2466
2467 o->flags &= ~DHOPT_ENCAP_MATCH;
2468
2469 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2470 continue;
2471
2472 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002473 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002474 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2475 {
2476 o->flags |= DHOPT_ENCAP_MATCH;
2477 found = 1;
2478 len += do_opt(o, NULL, NULL, 0) + 2;
2479 }
2480 }
2481
2482 if (found)
2483 {
2484 if (flags & DHOPT_ENCAPSULATE)
2485 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2486 else if (len > 250)
2487 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2488 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2489 {
2490 int swap_ent = htonl(opt->u.encap);
2491 memcpy(p, &swap_ent, 4);
2492 p += 4;
2493 *(p++) = len;
2494 for (o = config_opts; o; o = o->next)
2495 if (o->flags & DHOPT_ENCAP_MATCH)
2496 {
2497 len = do_opt(o, p + 2, NULL, 0);
2498 *(p++) = o->opt;
2499 *(p++) = len;
2500 p += len;
2501 }
2502 }
2503 }
2504 }
2505 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002506
Simon Kelley7de060b2011-08-26 17:24:52 +01002507 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002508
Simon Kelley316e2732010-01-22 20:16:09 +00002509 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002510 {
2511 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002512 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002513 }
2514
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002515 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2516 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2517 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2518 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2519 /* If we send vendor encapsulated options, and haven't already sent option 60,
2520 echo back the value we got from the client. */
2521 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2522
Simon Kelley7622fc02009-06-04 20:32:05 +01002523 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002524 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002525 {
2526 mess->file[0] = f0;
2527 mess->sname[0] = s0;
2528 }
2529}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002530
Simon Kelley7622fc02009-06-04 20:32:05 +01002531#endif
2532
2533
2534
2535
2536
2537
2538