blob: a49e076f963a212188f414bdc10dbca2bfbb8983 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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,
55 time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010056
Simon Kelley9009d742008-11-14 20:04:27 +000057
Simon Kelley6b010842007-02-12 20:32:07 +000058static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Simon Kelley8ef5ada2010-06-03 19:42:45 +010059static 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 +010060static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
61static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000062static 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 +010063struct dhcp_boot *find_boot(struct dhcp_netid *netid);
64
65
Simon Kelley824af852008-02-12 20:43:05 +000066size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley7de060b2011-08-26 17:24:52 +010067 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 +010068{
Simon Kelley26128d22004-11-14 16:43:54 +000069 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000070 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010071 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000072 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000073 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +010074 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000075 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000076 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010077 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000078 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000079 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010080 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000081 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000082 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000083 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010084 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010085 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010086 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010087 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010088 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010089 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000090 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010091 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000092 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000093 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010094 unsigned char pxe_uuid[17];
Simon Kelley316e2732010-01-22 20:16:09 +000095 unsigned char *oui = NULL, *serial = NULL, *class = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +010096
Simon Kelley1a6bca82008-07-11 11:11:42 +010097 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +000098
99 /* set tag with name == interface */
100 iface_id.net = iface_name;
101 iface_id.next = NULL;
102 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100103
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100104 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100105 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000106
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100107 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000108 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100109
Simon Kelley3be34542004-09-11 19:12:13 +0100110 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000111 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000112 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100113 u32 cookie = htonl(DHCP_COOKIE);
114
Simon Kelley3be34542004-09-11 19:12:13 +0100115 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100116 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100117 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100118
119 mess_type = option_uint(opt, 0, 1);
120
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100121 /* two things to note here: expand_buf may move the packet,
122 so reassign mess from daemon->packet. Also, the size
123 sent includes the IP and UDP headers, hence the magic "-28" */
124 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
125 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100126 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100127
128 if (size > DHCP_PACKET_MAX)
129 size = DHCP_PACKET_MAX;
130 else if (size < sizeof(struct dhcp_packet))
131 size = sizeof(struct dhcp_packet);
132
133 if (expand_buf(&daemon->dhcp_packet, size))
134 {
Simon Kelley824af852008-02-12 20:43:05 +0000135 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100136 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100137 }
138 }
139
Simon Kelley3be34542004-09-11 19:12:13 +0100140 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
141 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000142 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100143 mess->ciaddr.s_addr = 0;
144
Simon Kelley316e2732010-01-22 20:16:09 +0000145 /* search for device identity from CPEWAN devices, we pass this through to the script */
146 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
147 {
148 unsigned int elen, offset, len = option_len(opt);
149
150 for (offset = 0; offset < (len - 5); offset += elen + 5)
151 {
152 elen = option_uint(opt, offset + 4 , 1);
153 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
154 {
155 unsigned char *x = option_ptr(opt, offset + 5);
156 unsigned char *y = option_ptr(opt, offset + elen + 5);
157 oui = option_find1(x, y, 1, 1);
158 serial = option_find1(x, y, 2, 1);
159 class = option_find1(x, y, 3, 1);
160
161 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
162 the gateway id back. Note that the device class is optional */
163 if (oui && serial)
164 {
165 cpewan_id.net = "cpewan-id";
166 cpewan_id.next = netid;
167 netid = &cpewan_id;
168 }
169 break;
170 }
171 }
172 }
173
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100174 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
175 {
176 /* Any agent-id needs to be copied back out, verbatim, as the last option
177 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
178 get overwritten, then it will be shuffled back at the end of processing.
179 Note that the incoming options must not be overwritten here, so there has to
180 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100181 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100182 unsigned int total = option_len(opt) + 2;
183 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
184 if (last_opt && last_opt < end - total)
185 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100186 end -= total;
187 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100188 memcpy(agent_id, opt, total);
189 }
190
191 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100192 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 +0100193 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100194
195 /* look for RFC5107 server-identifier-override */
196 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
197 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100198
199 /* if a circuit-id or remote-is option is provided, exact-match to options. */
200 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
201 {
202 int search;
203
204 if (vendor->match_type == MATCH_CIRCUIT)
205 search = SUBOPT_CIRCUIT_ID;
206 else if (vendor->match_type == MATCH_REMOTE)
207 search = SUBOPT_REMOTE_ID;
208 else if (vendor->match_type == MATCH_SUBSCRIBER)
209 search = SUBOPT_SUBSCR_ID;
210 else
211 continue;
212
Simon Kelley1a6bca82008-07-11 11:11:42 +0100213 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100214 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100215 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100216 {
217 vendor->netid.next = netid;
218 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100219 }
220 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100221 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100222
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100223 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
224 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100225 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000226
227 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000228 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000229 {
Simon Kelley26128d22004-11-14 16:43:54 +0000230 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100231 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000232 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000233
Simon Kelley0a852542005-03-23 20:28:59 +0000234 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100235 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000236
237 /* If this request is missing a clid, but we've seen one before,
238 use it again for option matching etc. */
239 if (lease && !clid && lease->clid)
240 {
241 clid_len = lease->clid_len;
242 clid = lease->clid;
243 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000244
245 /* find mac to use for logging and hashing */
246 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000247 }
Simon Kelley3be34542004-09-11 19:12:13 +0100248
Simon Kelleycdeda282006-03-16 20:16:06 +0000249 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
250 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100251 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
252 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000253 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100254 mac->netid.next = netid;
255 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000256 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100257
Simon Kelley0a852542005-03-23 20:28:59 +0000258 /* Determine network for this packet. Our caller will have already linked all the
259 contexts which match the addresses of the receiving interface but if the
260 machine has an address already, or came via a relay, or we have a subnet selector,
261 we search again. If we don't have have a giaddr or explicit subnet selector,
262 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100263 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
264 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000265 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
266 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100267 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100268 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100269 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000270
Simon Kelley3d8df262005-08-29 12:19:27 +0100271 if (subnet_addr.s_addr)
272 {
273 addr = subnet_addr;
274 force = 1;
275 }
276 else if (mess->giaddr.s_addr)
277 {
278 addr = mess->giaddr;
279 force = 1;
280 }
Simon Kelley16972692006-10-16 20:04:18 +0100281 else
282 {
283 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
284 addr = mess->ciaddr;
285 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
286 if (context_tmp->netmask.s_addr &&
287 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
288 is_same_net(addr, context_tmp->end, context_tmp->netmask))
289 {
290 context_new = context;
291 break;
292 }
293 }
294
295 if (!context_new)
296 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100297 {
298 struct in_addr netmask = context_tmp->netmask;
299
300 /* guess the netmask for relayed networks */
301 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
302 {
303 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
304 netmask.s_addr = htonl(0xff000000);
305 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
306 netmask.s_addr = htonl(0xffff0000);
307 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
308 netmask.s_addr = htonl(0xffffff00);
309 }
310
311 /* This section fills in context mainly when a client which is on a remote (relayed)
312 network renews a lease without using the relay, after dnsmasq has restarted. */
313 if (netmask.s_addr != 0 &&
314 is_same_net(addr, context_tmp->start, netmask) &&
315 is_same_net(addr, context_tmp->end, netmask))
316 {
317 context_tmp->netmask = netmask;
318 if (context_tmp->local.s_addr == 0)
319 context_tmp->local = fallback;
320 if (context_tmp->router.s_addr == 0)
321 context_tmp->router = mess->giaddr;
322
323 /* fill in missing broadcast addresses for relayed ranges */
324 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
325 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
326
327 context_tmp->current = context_new;
328 context_new = context_tmp;
329 }
330 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100331
Simon Kelley3d8df262005-08-29 12:19:27 +0100332 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100333 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000334 }
Simon Kelley3be34542004-09-11 19:12:13 +0100335
336 if (!context)
337 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100338 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100339 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
340 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 +0100341 return 0;
342 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100343
Simon Kelley28866e92011-02-14 20:19:14 +0000344 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100345 {
346 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100347 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
348 {
349 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100350 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100351 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100352 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100353 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100354 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100355 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100356 }
357 }
Simon Kelley86e92f92013-04-23 11:31:39 +0100358
359 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
360 Otherwise assume the option is an array, and look for a matching element.
361 If no data given, existance of the option is enough. This code handles
362 rfc3925 V-I classes too. */
363 for (o = daemon->dhcp_match; o; o = o->next)
364 {
365 unsigned int len, elen, match = 0;
366 size_t offset, o2;
367
368 if (o->flags & DHOPT_RFC3925)
369 {
370 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
371 continue;
372
373 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
374 {
375 len = option_uint(opt, offset + 4 , 1);
376 /* Need to take care that bad data can't run us off the end of the packet */
377 if ((offset + len + 5 <= (option_len(opt))) &&
378 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
379 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
380 {
381 elen = option_uint(opt, o2, 1);
382 if ((o2 + elen + 1 <= option_len(opt)) &&
383 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
384 break;
385 }
386 if (match)
387 break;
388 }
389 }
390 else
391 {
392 if (!(opt = option_find(mess, sz, o->opt, 1)))
393 continue;
394
395 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
396 }
397
398 if (match)
399 {
400 o->netid->next = netid;
401 netid = o->netid;
402 }
403 }
404
405 /* user-class options are, according to RFC3004, supposed to contain
406 a set of counted strings. Here we check that this is so (by seeing
407 if the counts are consistent with the overall option length) and if
408 so zero the counts so that we don't get spurious matches between
409 the vendor string and the counts. If the lengths don't add up, we
410 assume that the option is a single string and non RFC3004 compliant
411 and just do the substring match. dhclient provides these broken options.
412 The code, later, which sends user-class data to the lease-change script
413 relies on the transformation done here.
414 */
415
416 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
417 {
418 unsigned char *ucp = option_ptr(opt, 0);
419 int tmp, j;
420 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
421 if (j == option_len(opt))
422 for (j = 0; j < option_len(opt); j = tmp)
423 {
424 tmp = j + ucp[j] + 1;
425 ucp[j] = 0;
426 }
427 }
428
429 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
430 {
431 int mopt;
432
433 if (vendor->match_type == MATCH_VENDOR)
434 mopt = OPTION_VENDOR_ID;
435 else if (vendor->match_type == MATCH_USER)
436 mopt = OPTION_USER_CLASS;
437 else
438 continue;
439
440 if ((opt = option_find(mess, sz, mopt, 1)))
441 {
442 int i;
443 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
444 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
445 {
446 vendor->netid.next = netid;
447 netid = &vendor->netid;
448 break;
449 }
450 }
451 }
452
453 /* mark vendor-encapsulated options which match the client-supplied vendor class,
454 save client-supplied vendor class */
455 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
456 {
457 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
458 vendor_class_len = option_len(opt);
459 }
460 match_vendor_opts(opt, daemon->dhcp_opts);
461
462 if (option_bool(OPT_LOG_OPTS))
463 {
464 if (sanitise(opt, daemon->namebuff))
465 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
466 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
467 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
468 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100469
Simon Kelley3be34542004-09-11 19:12:13 +0100470 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100471
Simon Kelleycdeda282006-03-16 20:16:06 +0000472 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
473 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100474
475 /* set "known" tag for known hosts */
476 if (config)
477 {
478 known_id.net = "known";
479 known_id.next = netid;
480 netid = &known_id;
481 }
Simon Kelley26128d22004-11-14 16:43:54 +0000482
Simon Kelley316e2732010-01-22 20:16:09 +0000483 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100484 {
485 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000486 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000487 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100488
489 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100490 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100491 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000492
Simon Kelley26128d22004-11-14 16:43:54 +0000493 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000494 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000495
496 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
497
498 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000499 {
500 hostname = config->hostname;
501 domain = config->domain;
502 }
503
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100504 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000505 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100506 struct dhcp_netid_list *list;
507
508 for (list = config->netid; list; list = list->next)
509 {
510 list->list->next = netid;
511 netid = list->list;
512 }
Simon Kelley26128d22004-11-14 16:43:54 +0000513 }
514
515 /* Match incoming filename field as a netid. */
516 if (mess->file[0])
517 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000518 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
519 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
520 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000521 id.next = netid;
522 netid = &id;
523 }
Simon Kelley6b010842007-02-12 20:32:07 +0000524
525 /* Add "bootp" as a tag to allow different options, address ranges etc
526 for BOOTP clients */
527 bootp_id.net = "bootp";
528 bootp_id.next = netid;
529 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000530
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100531 tagif_netid = run_tag_if(netid);
532
Simon Kelley26128d22004-11-14 16:43:54 +0000533 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100534 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100535 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000536
Simon Kelley3d8df262005-08-29 12:19:27 +0100537 if (!message)
538 {
Simon Kelley9009d742008-11-14 20:04:27 +0000539 int nailed = 0;
540
Simon Kelley3d8df262005-08-29 12:19:27 +0100541 if (have_config(config, CONFIG_ADDR))
542 {
Simon Kelley9009d742008-11-14 20:04:27 +0000543 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100544 logaddr = &config->addr;
545 mess->yiaddr = config->addr;
546 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000547 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100548 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000549 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000550 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100551 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100552 else
553 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000554 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100555 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000556 {
557 if (lease)
558 {
559 /* lease exists, wrong network. */
560 lease_prune(lease, now);
561 lease = NULL;
562 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100563 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000564 message = _("no address available");
565 }
566 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100567 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100568 }
569
Simon Kelley9009d742008-11-14 20:04:27 +0000570 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
571 message = _("wrong network");
572 else if (context->netid.net)
573 {
574 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100575 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100576 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100577
Simon Kelley4cb1b322012-02-06 14:30:41 +0000578 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100579
Simon Kelley9009d742008-11-14 20:04:27 +0000580 if (!message && !nailed)
581 {
582 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100583 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000584 break;
585 if (!id_list)
586 message = _("no address configured");
587 }
588
Simon Kelley7cebd202006-05-06 14:13:33 +0100589 if (!message &&
590 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000591 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000592 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000593
Simon Kelley3d8df262005-08-29 12:19:27 +0100594 if (!message)
595 {
596 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100597
Simon Kelleya9ab7322012-04-28 11:29:37 +0100598 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100599 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000600 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100601 /* infinite lease unless nailed in dhcp-host line. */
602 lease_set_expires(lease,
603 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
604 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000605 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100606
Simon Kelley7622fc02009-06-04 20:32:05 +0100607 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000608 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelley86e92f92013-04-23 11:31:39 +0100609 netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100610 }
611 }
612
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100613 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000614
Simon Kelley7de060b2011-08-26 17:24:52 +0100615 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100616 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000617
Roy Marples3f3adae2013-07-25 16:22:46 +0100618 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
Simon Kelley3d8df262005-08-29 12:19:27 +0100619 {
620 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
621 int len = option_len(opt);
622 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100623 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100624
Simon Kelley9fed0f72012-08-30 11:43:35 +0100625 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100626 len -= 3;
627 op += 3;
628 pp = op;
629
Simon Kelley9fed0f72012-08-30 11:43:35 +0100630 /* NB, the following always sets at least one bit */
631 if (option_bool(OPT_FQDN_UPDATE))
632 {
633 if (fqdn_flags & 0x01)
634 {
635 fqdn_flags |= 0x02; /* set O */
636 fqdn_flags &= ~0x01; /* clear S */
637 }
638 fqdn_flags |= 0x08; /* set N */
639 }
640 else
641 {
642 if (!(fqdn_flags & 0x01))
643 fqdn_flags |= 0x03; /* set S and O */
644 fqdn_flags &= ~0x08; /* clear N */
645 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100646
647 if (fqdn_flags & 0x04)
Roy Marples3f3adae2013-07-25 16:22:46 +0100648 while (*op != 0 && ((op + (*op)) - pp) < len)
Simon Kelley3d8df262005-08-29 12:19:27 +0100649 {
650 memcpy(pq, op+1, *op);
651 pq += *op;
652 op += (*op)+1;
653 *(pq++) = '.';
654 }
655 else
656 {
657 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000658 if (len > 0 && op[len-1] == 0)
659 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100660 pq += len + 1;
661 }
662
663 if (pq != daemon->dhcp_buff)
664 pq--;
665
666 *pq = 0;
667
Simon Kelley1f15b812009-10-13 17:49:32 +0100668 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100669 offer_hostname = client_hostname = daemon->dhcp_buff;
670 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000671 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000672 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000673 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100674 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000675 /* Microsoft clients are broken, and need zero-terminated strings
676 in options. We detect this state here, and do the same in
677 any options we send */
678 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
679 borken_opt = 1;
680 else
681 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100682 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100683 client_hostname = daemon->dhcp_buff;
684 }
685
Simon Kelley28866e92011-02-14 20:19:14 +0000686 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100687 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
688
Simon Kelley3d8df262005-08-29 12:19:27 +0100689 if (have_config(config, CONFIG_NAME))
690 {
691 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000692 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000693 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000694 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100695 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000696 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100697 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100698 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100699 {
Simon Kelley9009d742008-11-14 20:04:27 +0000700 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100701
702 if (strlen(client_hostname) != 0)
703 {
704 hostname = client_hostname;
705 if (!config)
706 {
707 /* Search again now we have a hostname.
708 Only accept configs without CLID and HWADDR here, (they won't match)
709 to avoid impersonation by name. */
710 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
711 mess->chaddr, mess->hlen,
712 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000713 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000714 {
715 config = new;
716 /* set "known" tag for known hosts */
717 known_id.net = "known";
718 known_id.next = netid;
719 netid = &known_id;
720 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100721 }
722 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000723 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100724
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100725 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100726 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100727 struct dhcp_netid_list *list;
728
729 for (list = config->netid; list; list = list->next)
730 {
731 list->list->next = netid;
732 netid = list->list;
733 }
Simon Kelleya2226412004-05-13 20:27:08 +0100734 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100735
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100736 tagif_netid = run_tag_if(netid);
Simon Kelley86e92f92013-04-23 11:31:39 +0100737
Simon Kelley26128d22004-11-14 16:43:54 +0000738 /* if all the netids in the ignore list are present, ignore this client */
739 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100740 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000741 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100742
743 /* If configured, we can override the server-id to be the address of the relay,
744 so that all traffic goes via the relay and can pick up agent-id info. This can be
745 configured for all relays, or by address. */
746 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
747 {
748 if (!daemon->override_relays)
749 override = mess->giaddr;
750 else
751 {
752 struct addr_list *l;
753 for (l = daemon->override_relays; l; l = l->next)
754 if (l->addr.s_addr == mess->giaddr.s_addr)
755 break;
756 if (l)
757 override = mess->giaddr;
758 }
759 }
760
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100761 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
762 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000763 clid = NULL;
764
Simon Kelley7622fc02009-06-04 20:32:05 +0100765 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100766 if (daemon->enable_pxe &&
767 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100768 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
769 {
770 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
771 {
772 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
773 uuid = pxe_uuid;
774 }
775
776 /* Check if this is really a PXE bootserver request, and handle specially if so. */
777 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
778 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
779 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
780 {
781 struct pxe_service *service;
782 int type = option_uint(opt, 0, 2);
783 int layer = option_uint(opt, 2, 2);
784 unsigned char save71[4];
785 struct dhcp_opt opt71;
786
Simon Kelley1f15b812009-10-13 17:49:32 +0100787 if (ignore)
788 return 0;
789
Simon Kelley7622fc02009-06-04 20:32:05 +0100790 if (layer & 0x8000)
791 {
792 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
793 return 0;
794 }
795
796 memcpy(save71, option_ptr(opt, 0), 4);
797
798 for (service = daemon->pxe_services; service; service = service->next)
799 if (service->type == type)
800 break;
801
802 if (!service || !service->basename)
803 return 0;
804
805 clear_packet(mess, end);
806
807 mess->yiaddr = mess->ciaddr;
808 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000809 if (service->sname)
810 mess->siaddr = a_record_from_hosts(service->sname, now);
811 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100812 mess->siaddr = service->server;
813 else
814 mess->siaddr = context->local;
815
816 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
817 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
818 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
819 pxe_misc(mess, end, uuid);
820
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100821 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100822 opt71.val = save71;
823 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
824 opt71.len = 4;
825 opt71.flags = DHOPT_VENDOR_MATCH;
826 opt71.netid = NULL;
827 opt71.next = daemon->dhcp_opts;
828 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
829
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100830 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000831 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100832 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 }
834
835 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
836 {
837 pxearch = option_uint(opt, 0, 2);
838
Simon Kelley316e2732010-01-22 20:16:09 +0000839 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000840 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100841 {
Simon Kelley28866e92011-02-14 20:19:14 +0000842 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100843
Simon Kelley28866e92011-02-14 20:19:14 +0000844 for (tmp = context; tmp; tmp = tmp->current)
845 if ((tmp->flags & CONTEXT_PROXY) &&
846 match_netid(tmp->filter, tagif_netid, 1))
847 break;
848
849 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100850 {
Simon Kelleya37cd7a2013-08-20 10:33:32 +0100851 struct dhcp_boot *boot;
852
853 if (tmp->netid.net)
854 {
855 tmp->netid.next = netid;
856 tagif_netid = run_tag_if(&tmp->netid);
857 }
858
859 boot = find_boot(tagif_netid);
860
Simon Kelley28866e92011-02-14 20:19:14 +0000861 mess->yiaddr.s_addr = 0;
862 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
863 {
864 mess->ciaddr.s_addr = 0;
865 mess->flags |= htons(0x8000); /* broadcast */
866 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100867
Simon Kelley28866e92011-02-14 20:19:14 +0000868 clear_packet(mess, end);
869
870 /* Provide the bootfile here, for gPXE, and in case we have no menu items
871 and set discovery_control = 8 */
872 if (boot)
873 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100874 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000875 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100876 else if (boot->tftp_sname)
877 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000878
879 if (boot->file)
880 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
881 }
882
883 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
884 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
885 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
886 pxe_misc(mess, end, uuid);
887 prune_vendor_opts(tagif_netid);
Simon Kelley751d6f42012-02-10 15:24:51 +0000888 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
Simon Kelley28866e92011-02-14 20:19:14 +0000889
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100890 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000891 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100892 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100893 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100894 }
895 }
896 }
897
898 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000899 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100900 return 0;
901
Simon Kelleybb01cb92004-12-13 20:56:23 +0000902 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100903 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100904 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100905 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000906 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100907 }
908
Simon Kelley3be34542004-09-11 19:12:13 +0100909 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000910 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000911 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000912 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000913 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000914 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100915
Simon Kelley44a2a312004-03-10 20:04:35 +0000916 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100917 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000918
Simon Kelleybb01cb92004-12-13 20:56:23 +0000919 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000920 return 0;
921
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100922 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 +0000923
924 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
925 lease_prune(lease, now);
926
Simon Kelley33820b72004-04-03 21:10:00 +0100927 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000928 config->addr.s_addr == option_addr(opt).s_addr)
929 {
Simon Kelley849a8352006-06-09 21:02:31 +0100930 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100931 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100932 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100933 config->flags |= CONFIG_DECLINED;
934 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000935 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100936 else
937 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100938 for (; context; context = context->current)
939 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000940
941 return 0;
942
943 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100944 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100945 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000946 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000947 return 0;
948
Simon Kelley44a2a312004-03-10 20:04:35 +0000949 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
950 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100951 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000952 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100953
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100954 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000955
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000956 return 0;
957
958 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100959 if (ignore || have_config(config, CONFIG_DISABLE))
960 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000961 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100962 opt = NULL;
963 }
964 else
965 {
966 struct in_addr addr, conf;
967
Simon Kelley1a6bca82008-07-11 11:11:42 +0100968 addr.s_addr = conf.s_addr = 0;
969
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100970 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
971 addr = option_addr(opt);
972
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100973 if (have_config(config, CONFIG_ADDR))
974 {
Simon Kelley849a8352006-06-09 21:02:31 +0100975 char *addrs = inet_ntoa(config->addr);
976
Simon Kelley9009d742008-11-14 20:04:27 +0000977 if ((ltmp = lease_find_by_addr(config->addr)) &&
978 ltmp != lease &&
979 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000980 {
981 int len;
982 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
983 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100984 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100985 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000986 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100987 else
988 {
989 struct dhcp_context *tmp;
990 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100991 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100992 break;
993 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100994 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 +0100995 else if (have_config(config, CONFIG_DECLINED) &&
996 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100997 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100998 else
999 conf = config->addr;
1000 }
1001 }
1002
1003 if (conf.s_addr)
1004 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +01001005 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001006 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001007 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001008 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001009 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001010 !config_find_by_address(daemon->dhcp_conf, addr))
1011 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001012 else if (emac_len == 0)
1013 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001014 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001015 message = _("no address available");
1016 }
1017
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001018 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 +01001019
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001020 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001021 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001022
Simon Kelleycdeda282006-03-16 20:16:06 +00001023 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001024 {
1025 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001026 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001027 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001028
Simon Kelley4cb1b322012-02-06 14:30:41 +00001029 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001030
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001031 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +01001032
Simon Kelley824af852008-02-12 20:43:05 +00001033 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001034 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001035 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001036 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001037 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001038 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001039 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001040 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001041 option_put(mess, end, OPTION_T1, 4, (time/2));
1042 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001043 }
Simon Kelley9009d742008-11-14 20:04:27 +00001044 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001045 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001046
Simon Kelley7de060b2011-08-26 17:24:52 +01001047 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001048
1049 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001050 if (ignore || have_config(config, CONFIG_DISABLE))
1051 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001052 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001053 {
1054 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001055 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001056
Simon Kelley4011c4e2006-10-28 16:26:19 +01001057 /* send vendor and user class info for new or recreated lease */
1058 do_classes = 1;
1059
Simon Kelleybb01cb92004-12-13 20:56:23 +00001060 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001061 {
Simon Kelley3be34542004-09-11 19:12:13 +01001062 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001063 selecting = 1;
1064
Simon Kelley1a6bca82008-07-11 11:11:42 +01001065 if (override.s_addr != 0)
1066 {
1067 if (option_addr(opt).s_addr != override.s_addr)
1068 return 0;
1069 }
Simon Kelley9009d742008-11-14 20:04:27 +00001070 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001071 {
1072 for (; context; context = context->current)
1073 if (context->local.s_addr == option_addr(opt).s_addr)
1074 break;
1075
1076 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001077 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001078 /* Handle very strange configs where clients have more than one route to the server.
1079 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1080 Have to set override to make sure we echo back the correct server-id */
1081 struct irec *intr;
1082
Simon Kelley115ac3e2013-05-20 11:28:32 +01001083 enumerate_interfaces(0);
Simon Kelley7de060b2011-08-26 17:24:52 +01001084
1085 for (intr = daemon->interfaces; intr; intr = intr->next)
1086 if (intr->addr.sa.sa_family == AF_INET &&
1087 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1088 intr->tftp_ok)
1089 break;
1090
1091 if (intr)
1092 override = intr->addr.in.sin_addr;
1093 else
1094 {
1095 /* In auth mode, a REQUEST sent to the wrong server
1096 should be faulted, so that the client establishes
1097 communication with us, otherwise, silently ignore. */
1098 if (!option_bool(OPT_AUTHORITATIVE))
1099 return 0;
1100 message = _("wrong server-ID");
1101 }
Simon Kelley9009d742008-11-14 20:04:27 +00001102 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001103 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001104
Simon Kelley3be34542004-09-11 19:12:13 +01001105 /* If a lease exists for this host and another address, squash it. */
1106 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1107 {
1108 lease_prune(lease, now);
1109 lease = NULL;
1110 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001111 }
Simon Kelley3be34542004-09-11 19:12:13 +01001112 else
1113 {
1114 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001115 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001116 return 0;
1117
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001118 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001119 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001120 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001121 }
1122 else
1123 {
1124 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001125 /* Check existing lease for this address.
1126 We allow it to be missing if dhcp-authoritative mode
1127 as long as we can allocate the lease now - checked below.
1128 This makes for a smooth recovery from a lost lease DB */
1129 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001130 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001131 {
Simon Kelley28866e92011-02-14 20:19:14 +00001132 /* A client rebinding will broadcast the request, so we may see it even
1133 if the lease is held by another server. Just ignore it in that case.
1134 If the request is unicast to us, then somethings wrong, NAK */
1135 if (!unicast_dest)
1136 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001137 message = _("lease not found");
1138 /* ensure we broadcast NAK */
1139 unicast_dest = 0;
1140 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001141
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001142 /* desynchronise renewals */
1143 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001144 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001145 }
1146
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001147 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001148
Simon Kelleydfa666f2004-08-02 18:27:27 +01001149 if (!message)
1150 {
1151 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001152 struct dhcp_context *tmp = NULL;
1153
1154 if (have_config(config, CONFIG_ADDR))
1155 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001156 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001157 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001158
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001159 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001160 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001161 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001162 message = _("wrong network");
1163 /* ensure we broadcast NAK */
1164 unicast_dest = 0;
1165 }
1166
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001167 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001168 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001169 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001170 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001171
Simon Kelleydfa666f2004-08-02 18:27:27 +01001172 /* Check if a new static address has been configured. Be very sure that
1173 when the client does DISCOVER, it will get the static address, otherwise
1174 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001175 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001176 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001177 (!have_config(config, CONFIG_DECLINED) ||
1178 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001179 config->addr.s_addr != mess->yiaddr.s_addr &&
1180 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001181 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001182
1183 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001184 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001185 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001186
Simon Kelley9009d742008-11-14 20:04:27 +00001187 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1188 {
1189 /* If a host is configured with more than one MAC address, it's OK to 'nix
1190 a lease from one of it's MACs to give the address to another. */
1191 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1192 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001193 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001194 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1195 inet_ntoa(ltmp->addr));
1196 lease = ltmp;
1197 }
Simon Kelley16972692006-10-16 20:04:18 +01001198 else
Simon Kelley9009d742008-11-14 20:04:27 +00001199 message = _("address in use");
1200 }
1201
1202 if (!message)
1203 {
1204 if (emac_len == 0)
1205 message = _("no unique-id");
1206
1207 else if (!lease)
1208 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001209 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001210 do_classes = 1;
1211 else
1212 message = _("no leases left");
1213 }
Simon Kelley16972692006-10-16 20:04:18 +01001214 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001215 }
Simon Kelley16972692006-10-16 20:04:18 +01001216
Simon Kelley44a2a312004-03-10 20:04:35 +00001217 if (message)
1218 {
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001219 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001220
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001221 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001222 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001223 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001224 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001225 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001226 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1227 a distant subnet which unicast a REQ to us won't work. */
1228 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1229 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1230 {
1231 mess->flags |= htons(0x8000); /* broadcast */
1232 mess->ciaddr.s_addr = 0;
1233 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001234 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001235 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001236 {
Simon Kelley316e2732010-01-22 20:16:09 +00001237 if (context->netid.net)
1238 {
1239 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001240 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001241 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001242
Simon Kelley4cb1b322012-02-06 14:30:41 +00001243 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001244
Simon Kelleydcffad22012-04-24 15:25:18 +01001245 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001246 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001247 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001248 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001249
Simon Kelleydcffad22012-04-24 15:25:18 +01001250#ifdef HAVE_SCRIPT
1251 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001252 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001253 struct dhcp_netid *n;
1254
1255 if (mess->giaddr.s_addr)
1256 lease->giaddr = mess->giaddr;
1257
1258 free(lease->extradata);
1259 lease->extradata = NULL;
1260 lease->extradata_size = lease->extradata_len = 0;
1261
1262 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1263 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1264 add_extradata_opt(lease, oui);
1265 add_extradata_opt(lease, serial);
1266 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001267
1268 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1269 {
1270 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1271 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1272 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1273 }
1274 else
1275 {
1276 add_extradata_opt(lease, NULL);
1277 add_extradata_opt(lease, NULL);
1278 add_extradata_opt(lease, NULL);
1279 }
1280
Simon Kelleydcffad22012-04-24 15:25:18 +01001281 /* space-concat tag set */
1282 if (!tagif_netid)
1283 add_extradata_opt(lease, NULL);
1284 else
1285 for (n = tagif_netid; n; n = n->next)
1286 {
1287 struct dhcp_netid *n1;
1288 /* kill dupes */
1289 for (n1 = n->next; n1; n1 = n1->next)
1290 if (strcmp(n->net, n1->net) == 0)
1291 break;
1292 if (!n1)
1293 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1294 }
1295
1296 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1297 {
1298 int len = option_len(opt);
1299 unsigned char *ucp = option_ptr(opt, 0);
1300 /* If the user-class option started as counted strings, the first byte will be zero. */
1301 if (len != 0 && ucp[0] == 0)
1302 ucp++, len--;
1303 lease_add_extradata(lease, ucp, len, 0);
1304 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001305 }
Simon Kelley316e2732010-01-22 20:16:09 +00001306#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001307 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001308
1309 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1310 {
1311 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001312 hostname = client_hostname;
1313 hostname_auth = 1;
1314 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001315
Simon Kelley824af852008-02-12 20:43:05 +00001316 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001317 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001318
Simon Kelley832af0b2007-01-21 20:01:28 +00001319 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1320 if (!hostname_auth)
1321 {
1322 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001323 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001324 break;
1325 if (id_list)
1326 hostname = NULL;
1327 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001328
1329 /* Last ditch, if configured, generate hostname from mac address */
1330 if (!hostname && emac_len != 0)
1331 {
1332 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1333 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1334 break;
1335 if (id_list)
1336 {
1337 int i;
1338
1339 hostname = daemon->dhcp_buff;
1340 /* buffer is 256 bytes, 3 bytes per octet */
1341 for (i = 0; (i < emac_len) && (i < 80); i++)
1342 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1343 hostname = daemon->dhcp_buff;
1344 }
1345 }
1346
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001347 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001348 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001349
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001350 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001351 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001352
1353 if (override.s_addr != 0)
1354 lease->override = override;
1355 else
1356 override = lease->override;
1357
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001358 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001359
Simon Kelley7622fc02009-06-04 20:32:05 +01001360 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001361 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001362 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001363 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001364 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001365 {
Simon Kelley59353a62004-11-21 19:34:28 +00001366 while (fuzz > (time/16))
1367 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001368 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1369 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001370 }
Simon Kelley9009d742008-11-14 20:04:27 +00001371 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001372 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001373 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001374
Simon Kelley7de060b2011-08-26 17:24:52 +01001375 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001376
1377 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001378 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001379 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001380
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001381 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001382
Simon Kelley73a08a22009-02-05 20:28:08 +00001383 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001384 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001385
1386 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001387 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001388
Simon Kelley5aabfc72007-08-29 11:24:47 +01001389 /* Find a least based on IP address if we didn't
1390 get one from MAC address/client-d */
1391 if (!lease &&
1392 (lease = lease_find_by_addr(mess->ciaddr)) &&
1393 lease->hostname)
1394 hostname = lease->hostname;
1395
Simon Kelley0f371f92013-07-27 15:15:38 +01001396 if (!hostname)
1397 hostname = host_from_dns(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001398
Simon Kelley73a08a22009-02-05 20:28:08 +00001399 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001400 {
1401 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001402 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001403 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001404
Simon Kelley4cb1b322012-02-06 14:30:41 +00001405 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001406
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001407 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001408
Simon Kelley3927da42008-07-20 15:10:39 +01001409 if (lease)
1410 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001411 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001412 if (override.s_addr != 0)
1413 lease->override = override;
1414 else
1415 override = lease->override;
1416 }
1417
Simon Kelley7622fc02009-06-04 20:32:05 +01001418 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001419 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001420 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001421
1422 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1423 we supply a utility which makes DHCPINFORM requests to get this information.
1424 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1425 which won't be true for ordinary clients, but will be true for the
1426 dhcp_lease_time utility. */
1427 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1428 {
1429 if (lease->expires == 0)
1430 time = 0xffffffff;
1431 else
1432 time = (unsigned int)difftime(lease->expires, now);
1433 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1434 }
1435
Simon Kelley9009d742008-11-14 20:04:27 +00001436 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001437 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001438
Simon Kelley5aabfc72007-08-29 11:24:47 +01001439 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001440 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001441 }
1442
1443 return 0;
1444}
1445
Simon Kelley6b010842007-02-12 20:32:07 +00001446/* find a good value to use as MAC address for logging and address-allocation hashing.
1447 This is normally just the chaddr field from the DHCP packet,
1448 but eg Firewire will have hlen == 0 and use the client-id instead.
1449 This could be anything, but will normally be EUI64 for Firewire.
1450 We assume that if the first byte of the client-id equals the htype byte
1451 then the client-id is using the usual encoding and use the rest of the
1452 client-id: if not we can use the whole client-id. This should give
1453 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001454unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001455 int clid_len, unsigned char *clid, int *len_out)
1456{
1457 if (hwlen == 0 && clid && clid_len > 3)
1458 {
1459 if (clid[0] == hwtype)
1460 {
1461 *len_out = clid_len - 1 ;
1462 return clid + 1;
1463 }
1464
1465#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1466 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1467 {
1468 *len_out = clid_len - 1 ;
1469 return clid + 1;
1470 }
1471#endif
1472
1473 *len_out = clid_len;
1474 return clid;
1475 }
1476
1477 *len_out = hwlen;
1478 return hwaddr;
1479}
1480
Simon Kelley824af852008-02-12 20:43:05 +00001481static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001482{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001483 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001484
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001485 if (opt)
1486 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001487 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001488 if (req_time < 120 )
1489 req_time = 120; /* sanity */
1490 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1491 time = req_time;
1492 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001493
1494 return time;
1495}
1496
Simon Kelley73a08a22009-02-05 20:28:08 +00001497static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001498{
Simon Kelley73a08a22009-02-05 20:28:08 +00001499 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001500 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001501 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001502 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001503 else
1504 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001505}
1506
Simon Kelleyf2621c72007-04-29 19:47:21 +01001507static int sanitise(unsigned char *opt, char *buf)
1508{
1509 char *p;
1510 int i;
1511
1512 *buf = 0;
1513
1514 if (!opt)
1515 return 0;
1516
Simon Kelley1a6bca82008-07-11 11:11:42 +01001517 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001518
1519 for (i = option_len(opt); i > 0; i--)
1520 {
1521 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001522 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001523 *buf++ = c;
1524 }
1525 *buf = 0; /* add terminator */
1526
1527 return 1;
1528}
1529
Simon Kelley316e2732010-01-22 20:16:09 +00001530#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001531static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1532{
1533 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001534 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001535 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001536 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001537}
1538#endif
1539
Simon Kelley5aabfc72007-08-29 11:24:47 +01001540static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001541 int mac_len, char *interface, char *string, char *err, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001542{
Simon Kelley16972692006-10-16 20:04:18 +01001543 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001544
Kevin Darbyshire-Bryant227ddad2013-10-24 17:47:00 +01001545 if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001546 return;
1547
Simon Kelley16972692006-10-16 20:04:18 +01001548 /* addr may be misaligned */
1549 if (addr)
1550 memcpy(&a, addr, sizeof(a));
1551
Simon Kelley7622fc02009-06-04 20:32:05 +01001552 print_mac(daemon->namebuff, ext_mac, mac_len);
1553
Simon Kelley28866e92011-02-14 20:19:14 +00001554 if(option_bool(OPT_LOG_OPTS))
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001555 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001556 ntohl(xid),
1557 type,
1558 interface,
1559 addr ? inet_ntoa(a) : "",
1560 addr ? " " : "",
1561 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001562 string ? string : "",
1563 err ? err : "");
Simon Kelley7622fc02009-06-04 20:32:05 +01001564 else
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001565 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
Simon Kelley7622fc02009-06-04 20:32:05 +01001566 type,
1567 interface,
1568 addr ? inet_ntoa(a) : "",
1569 addr ? " " : "",
1570 daemon->namebuff,
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +01001571 string ? string : "",
1572 err ? err : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001573}
1574
Simon Kelley7622fc02009-06-04 20:32:05 +01001575static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001576{
1577 while (*start != OPTION_END)
1578 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001579 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 +01001580
Simon Kelley4cb1b322012-02-06 14:30:41 +00001581 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1582 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001583 start += start[1] + 2;
1584 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001585}
1586
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001587static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001588{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001589 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001590 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001591 if (p > end)
1592 return NULL;
1593 else if (*p == OPTION_END)
1594 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001595 else if (*p == OPTION_PAD)
1596 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001597 else
1598 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001599 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001600 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001601 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001602 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001603 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001604 return NULL; /* malformed packet */
1605 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001606 return p;
1607 p += opt_len + 2;
1608 }
1609 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001610}
1611
Simon Kelleycdeda282006-03-16 20:16:06 +00001612static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001613{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001614 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001615
Simon Kelley3be34542004-09-11 19:12:13 +01001616 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001617 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1618 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001619
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001620 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001621 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001622 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001623
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001624 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001625 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001626 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1627 return ret;
1628
1629 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001630 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001631 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1632 return ret;
1633
1634 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001635}
1636
Simon Kelley4cb1b322012-02-06 14:30:41 +00001637static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001638{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001639 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001640 /* struct in_addr is network byte order */
1641 struct in_addr ret;
1642
Simon Kelley4cb1b322012-02-06 14:30:41 +00001643 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001644
1645 return ret;
1646}
1647
Simon Kelley7622fc02009-06-04 20:32:05 +01001648static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001649{
1650 /* this worries about unaligned data and byte order */
1651 unsigned int ret = 0;
1652 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001653 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001654
1655 for (i = 0; i < size; i++)
1656 ret = (ret << 8) | *p++;
1657
1658 return ret;
1659}
1660
1661static unsigned char *dhcp_skip_opts(unsigned char *start)
1662{
1663 while (*start != 0)
1664 start += start[1] + 2;
1665 return start;
1666}
1667
1668/* only for use when building packet: doesn't check for bad data. */
1669static unsigned char *find_overload(struct dhcp_packet *mess)
1670{
1671 unsigned char *p = &mess->options[0] + sizeof(u32);
1672
1673 while (*p != 0)
1674 {
1675 if (*p == OPTION_OVERLOAD)
1676 return p;
1677 p += p[1] + 2;
1678 }
1679 return NULL;
1680}
1681
Simon Kelley7de060b2011-08-26 17:24:52 +01001682static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1683{
1684 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1685 unsigned char *overload;
1686 size_t ret;
1687
1688 /* move agent_id back down to the end of the packet */
1689 if (agent_id)
1690 {
1691 memmove(p, agent_id, real_end - agent_id);
1692 p += real_end - agent_id;
1693 memset(p, 0, real_end - p); /* in case of overlap */
1694 }
1695
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001696 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001697 overload = find_overload(mess);
1698
1699 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001700 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001701 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001702 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001703 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001704 }
Simon Kelley28866e92011-02-14 20:19:14 +00001705 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001706 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1707
1708 if (overload && (option_uint(overload, 0, 1) & 2))
1709 {
1710 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001711 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001712 log_options(mess->sname, mess->xid);
1713 }
Simon Kelley28866e92011-02-14 20:19:14 +00001714 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001715 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1716
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001717
1718 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001719
Simon Kelley28866e92011-02-14 20:19:14 +00001720 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001721 {
1722 if (mess->siaddr.s_addr != 0)
1723 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1724
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001725 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1726 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1727
Simon Kelley7622fc02009-06-04 20:32:05 +01001728 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1729 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001730
1731 ret = (size_t)(p - (unsigned char *)mess);
1732
1733 if (ret < MIN_PACKETSZ)
1734 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001735
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001736 return ret;
1737}
1738
1739static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1740{
1741 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1742
1743 if (p + len + 3 >= end)
1744 /* not enough space in options area, try and use overload, if poss */
1745 {
1746 unsigned char *overload;
1747
1748 if (!(overload = find_overload(mess)) &&
1749 (mess->file[0] == 0 || mess->sname[0] == 0))
1750 {
1751 /* attempt to overload fname and sname areas, we've reserved space for the
1752 overflow option previuously. */
1753 overload = p;
1754 *(p++) = OPTION_OVERLOAD;
1755 *(p++) = 1;
1756 }
1757
1758 p = NULL;
1759
1760 /* using filename field ? */
1761 if (overload)
1762 {
1763 if (mess->file[0] == 0)
1764 overload[2] |= 1;
1765
1766 if (overload[2] & 1)
1767 {
1768 p = dhcp_skip_opts(mess->file);
1769 if (p + len + 3 >= mess->file + sizeof(mess->file))
1770 p = NULL;
1771 }
1772
1773 if (!p)
1774 {
1775 /* try to bring sname into play (it may be already) */
1776 if (mess->sname[0] == 0)
1777 overload[2] |= 2;
1778
1779 if (overload[2] & 2)
1780 {
1781 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001782 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001783 p = NULL;
1784 }
1785 }
1786 }
1787
1788 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001789 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 +00001790 }
1791
1792 if (p)
1793 {
1794 *(p++) = opt;
1795 *(p++) = len;
1796 }
1797
1798 return p;
1799}
1800
1801static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1802{
1803 int i;
1804 unsigned char *p = free_space(mess, end, opt, len);
1805
1806 if (p)
1807 for (i = 0; i < len; i++)
1808 *(p++) = val >> (8 * (len - (i + 1)));
1809}
1810
1811static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1812 char *string, int null_term)
1813{
1814 unsigned char *p;
1815 size_t len = strlen(string);
1816
1817 if (null_term && len != 255)
1818 len++;
1819
1820 if ((p = free_space(mess, end, opt, len)))
1821 memcpy(p, string, len);
1822}
1823
1824/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001825static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001826{
1827 int len = opt->len;
1828
1829 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1830 len++;
1831
1832 if (p && len != 0)
1833 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001834 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001835 {
1836 int j;
1837 struct in_addr *a = (struct in_addr *)opt->val;
1838 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1839 {
1840 /* zero means "self" (but not in vendorclass options.) */
1841 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001842 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001843 else
1844 memcpy(p, a, INADDRSZ);
1845 p += INADDRSZ;
1846 }
1847 }
1848 else
Simon Kelley625ac282013-07-02 21:19:32 +01001849 /* empty string may be extended to "\0" by null_term */
1850 memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001851 }
1852 return len;
1853}
Simon Kelley7622fc02009-06-04 20:32:05 +01001854
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001855static int in_list(unsigned char *list, int opt)
1856{
1857 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001858
1859 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001860 if (!list)
1861 return 1;
1862
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001863 for (i = 0; list[i] != OPTION_END; i++)
1864 if (opt == list[i])
1865 return 1;
1866
1867 return 0;
1868}
1869
Simon Kelley7de060b2011-08-26 17:24:52 +01001870static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001871{
Simon Kelley7de060b2011-08-26 17:24:52 +01001872 struct dhcp_opt *opts;
1873
1874 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1875 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1876 return opts;
1877
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001878 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001879}
1880
Simon Kelley6b010842007-02-12 20:32:07 +00001881/* mark vendor-encapsulated options which match the client-supplied or
1882 config-supplied vendor class */
1883static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1884{
1885 for (; dopt; dopt = dopt->next)
1886 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001887 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001888 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001889 {
1890 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001891 if (dopt->u.vendor_class)
1892 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001893 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001894 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001895 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001896 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001897 break;
1898 }
1899 }
1900 }
1901}
1902
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001903static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1904 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001905{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001906 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001907 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001908 unsigned char *p;
1909
1910 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001911 for (enc_len = 0, start = opt; opt; opt = opt->next)
1912 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001913 {
1914 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001915 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001916 if (enc_len + new <= 255)
1917 enc_len += new;
1918 else
1919 {
1920 p = free_space(mess, end, encap, enc_len);
1921 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001922 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001923 {
1924 len = do_opt(start, p + 2, NULL, null_term);
1925 *(p++) = start->opt;
1926 *(p++) = len;
1927 p += len;
1928 }
1929 enc_len = new;
1930 start = opt;
1931 }
1932 }
1933
1934 if (enc_len != 0 &&
1935 (p = free_space(mess, end, encap, enc_len + 1)))
1936 {
1937 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001938 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001939 {
1940 len = do_opt(start, p + 2, NULL, null_term);
1941 *(p++) = start->opt;
1942 *(p++) = len;
1943 p += len;
1944 }
1945 *p = OPTION_END;
1946 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001947
1948 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00001949}
1950
Simon Kelley7622fc02009-06-04 20:32:05 +01001951static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01001952{
Simon Kelley7622fc02009-06-04 20:32:05 +01001953 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01001954
Simon Kelley7622fc02009-06-04 20:32:05 +01001955 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1956 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1957 memcpy(p, uuid, 17);
1958}
1959
1960static int prune_vendor_opts(struct dhcp_netid *netid)
1961{
1962 int force = 0;
1963 struct dhcp_opt *opt;
1964
1965 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1966 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1967 if (opt->flags & DHOPT_VENDOR_MATCH)
1968 {
1969 if (!match_netid(opt->netid, netid, 1))
1970 opt->flags &= ~DHOPT_VENDOR_MATCH;
1971 else if (opt->flags & DHOPT_FORCE)
1972 force = 1;
1973 }
1974 return force;
1975}
1976
Simon Kelley751d6f42012-02-10 15:24:51 +00001977static 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 +01001978{
1979#define NUM_OPTS 4
1980
1981 unsigned char *p, *q;
1982 struct pxe_service *service;
1983 static struct dhcp_opt *o, *ret;
1984 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001985 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01001986
1987 /* We pass back references to these, hence they are declared static */
1988 static unsigned char discovery_control;
1989 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1990 static struct dhcp_opt *fake_opts = NULL;
1991
Simon Kelley316e2732010-01-22 20:16:09 +00001992 /* Disable multicast, since we don't support it, and broadcast
1993 unless we need it */
1994 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001995
1996 ret = daemon->dhcp_opts;
1997
1998 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
1999 return ret;
2000
2001 for (i = 0; i < NUM_OPTS; i++)
2002 {
2003 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2004 fake_opts[i].netid = NULL;
2005 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2006 }
2007
2008 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2009 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002010 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002011
2012 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2013 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2014 {
2015 size_t len = strlen(service->menu);
2016 /* opt 43 max size is 255. encapsulated option has type and length
2017 bytes, so its max size is 253. */
2018 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2019 {
2020 *(p++) = service->type >> 8;
2021 *(p++) = service->type;
2022 *(p++) = len;
2023 memcpy(p, service->menu, len);
2024 p += len;
2025 i++;
2026 }
2027 else
2028 {
2029 toobig:
2030 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2031 return daemon->dhcp_opts;
2032 }
2033
Simon Kelley751d6f42012-02-10 15:24:51 +00002034 boot_server = service->basename ? local :
2035 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2036
Simon Kelley316e2732010-01-22 20:16:09 +00002037 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002038 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002039 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002040 goto toobig;
2041
2042 /* Boot service with known address - give it */
2043 *(q++) = service->type >> 8;
2044 *(q++) = service->type;
2045 *(q++) = 1;
2046 /* dest misaligned */
2047 memcpy(q, &boot_server.s_addr, INADDRSZ);
2048 q += INADDRSZ;
2049 }
2050 else if (service->type != 0)
2051 /* We don't know the server for a service type, so we'll
2052 allow the client to broadcast for it */
2053 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002054 }
2055
2056 /* if no prompt, wait forever if there's a choice */
2057 fake_prompt[0] = (i > 1) ? 255 : 0;
2058
2059 if (i == 0)
2060 discovery_control = 8; /* no menu - just use use mess->filename */
2061 else
2062 {
2063 ret = &fake_opts[j--];
2064 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2065 ret->val = (unsigned char *)daemon->dhcp_buff;
2066 ret->opt = SUBOPT_PXE_MENU;
2067
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002068 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002069 {
2070 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002071 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2072 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002073 ret->opt = SUBOPT_PXE_SERVERS;
2074 }
2075 }
2076
2077 for (o = daemon->dhcp_opts; o; o = o->next)
2078 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2079 break;
2080
2081 if (!o)
2082 {
2083 ret = &fake_opts[j--];
2084 ret->len = sizeof(fake_prompt);
2085 ret->val = fake_prompt;
2086 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2087 }
2088
Simon Kelley316e2732010-01-22 20:16:09 +00002089 ret = &fake_opts[j--];
2090 ret->len = 1;
2091 ret->opt = SUBOPT_PXE_DISCOVERY;
2092 ret->val= &discovery_control;
2093
Simon Kelley7622fc02009-06-04 20:32:05 +01002094 return ret;
2095}
2096
2097static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2098{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002099 memset(mess->sname, 0, sizeof(mess->sname));
2100 memset(mess->file, 0, sizeof(mess->file));
2101 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2102 mess->siaddr.s_addr = 0;
2103}
Simon Kelleycdeda282006-03-16 20:16:06 +00002104
Simon Kelley7622fc02009-06-04 20:32:05 +01002105struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002106{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002107 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002108
2109 /* decide which dhcp-boot option we're using */
2110 for (boot = daemon->boot_config; boot; boot = boot->next)
2111 if (match_netid(boot->netid, netid, 0))
2112 break;
2113 if (!boot)
2114 /* No match, look for one without a netid */
2115 for (boot = daemon->boot_config; boot; boot = boot->next)
2116 if (match_netid(boot->netid, netid, 1))
2117 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002118
2119 return boot;
2120}
2121
2122static void do_options(struct dhcp_context *context,
2123 struct dhcp_packet *mess,
2124 unsigned char *end,
2125 unsigned char *req_options,
2126 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002127 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002128 struct dhcp_netid *netid,
2129 struct in_addr subnet_addr,
2130 unsigned char fqdn_flags,
2131 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002132 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002133 int vendor_class_len,
2134 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002135{
2136 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2137 struct dhcp_boot *boot;
2138 unsigned char *p;
2139 int i, len, force_encap = 0;
2140 unsigned char f0 = 0, s0 = 0;
2141 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002142 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002143 struct dhcp_netid *tagif;
2144 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002145
Simon Kelley4cb1b322012-02-06 14:30:41 +00002146 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002147 if (context)
2148 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002149 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002150
Simon Kelley7622fc02009-06-04 20:32:05 +01002151 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002152 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002153 {
2154 char *q = daemon->namebuff;
2155 for (i = 0; req_options[i] != OPTION_END; i++)
2156 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002157 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002158 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2159 "%d%s%s%s",
2160 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002161 strlen(s) != 0 ? ":" : "",
2162 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002163 req_options[i+1] == OPTION_END ? "" : ", ");
2164 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2165 {
2166 q = daemon->namebuff;
2167 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2168 }
2169 }
2170 }
2171
Simon Kelley7de060b2011-08-26 17:24:52 +01002172 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2173 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2174 break;
2175 if (id_list)
2176 mess->flags |= htons(0x8000); /* force broadcast */
2177
Simon Kelley73a08a22009-02-05 20:28:08 +00002178 if (context)
2179 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002180
2181 /* See if we can send the boot stuff as options.
2182 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002183 and very old DHCP clients won't have this, we also
2184 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002185 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002186 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002187 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002188 {
2189 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002190 {
Simon Kelley28866e92011-02-14 20:19:14 +00002191 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002192 req_options &&
2193 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002194 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2195 else
Simon Kelley824af852008-02-12 20:43:05 +00002196 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002197 }
2198
2199 if (boot->file)
2200 {
Simon Kelley28866e92011-02-14 20:19:14 +00002201 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002202 req_options &&
2203 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002204 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2205 else
Simon Kelley824af852008-02-12 20:43:05 +00002206 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002207 }
2208
Simon Kelley7de060b2011-08-26 17:24:52 +01002209 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002210 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002211 else if (boot->tftp_sname)
2212 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002213 }
Simon Kelley824af852008-02-12 20:43:05 +00002214 else
2215 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002216 they're not explicitly asked for as options. OPTION_END is used
2217 as an internal way to specify siaddr without using dhcp-boot, for use in
2218 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002219 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002220 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002221 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002222 {
2223 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2224 done_file = 1;
2225 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002226
Simon Kelley824af852008-02-12 20:43:05 +00002227 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002228 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002229 {
2230 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2231 done_server = 1;
2232 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002233
Simon Kelley7de060b2011-08-26 17:24:52 +01002234 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002235 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002236 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002237
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002238 /* We don't want to do option-overload for BOOTP, so make the file and sname
2239 fields look like they are in use, even when they aren't. This gets restored
2240 at the end of this function. */
2241
Simon Kelley28866e92011-02-14 20:19:14 +00002242 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002243 {
2244 f0 = mess->file[0];
2245 mess->file[0] = 1;
2246 s0 = mess->sname[0];
2247 mess->sname[0] = 1;
2248 }
2249
2250 /* At this point, if mess->sname or mess->file are zeroed, they are available
2251 for option overload, reserve space for the overload option. */
2252 if (mess->file[0] == 0 || mess->sname[0] == 0)
2253 end -= 3;
2254
Simon Kelley3be34542004-09-11 19:12:13 +01002255 /* rfc3011 says this doesn't need to be in the requested options list. */
2256 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002257 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002258
2259 /* replies to DHCPINFORM may not have a valid context */
2260 if (context)
2261 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002262 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002263 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2264
2265 /* May not have a "guessed" broadcast address if we got no packets via a relay
2266 from this net yet (ie just unicast renewals after a restart */
2267 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002268 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002269 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2270
2271 /* Same comments as broadcast apply, and also may not be able to get a sensible
2272 default when using subnet select. User must configure by steam in that case. */
2273 if (context->router.s_addr &&
2274 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002275 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002276 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2277
Simon Kelleya21e27b2013-02-17 16:41:35 +00002278 if (daemon->port == NAMESERVER_PORT &&
2279 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002280 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002281 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2282 }
Simon Kelley3be34542004-09-11 19:12:13 +01002283
Simon Kelley9009d742008-11-14 20:04:27 +00002284 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002285 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002286 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002287
Simon Kelley824af852008-02-12 20:43:05 +00002288 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002289 if (hostname)
2290 {
Simon Kelley824af852008-02-12 20:43:05 +00002291 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002292 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002293 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002294
2295 if (fqdn_flags != 0)
2296 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002297 len = strlen(hostname) + 3;
2298
Simon Kelley3d8df262005-08-29 12:19:27 +01002299 if (fqdn_flags & 0x04)
2300 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002301 else if (null_term)
2302 len++;
2303
Simon Kelley9009d742008-11-14 20:04:27 +00002304 if (domain)
2305 len += strlen(domain) + 1;
Roy Marples3f3adae2013-07-25 16:22:46 +01002306 else if (fqdn_flags & 0x04)
2307 len--;
2308
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002309 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002310 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002311 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002312 *(p++) = 255;
2313 *(p++) = 255;
2314
2315 if (fqdn_flags & 0x04)
2316 {
2317 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002318 if (domain)
Roy Marples3f3adae2013-07-25 16:22:46 +01002319 {
2320 p = do_rfc1035_name(p, domain);
2321 *p++ = 0;
2322 }
Simon Kelley3d8df262005-08-29 12:19:27 +01002323 }
2324 else
2325 {
2326 memcpy(p, hostname, strlen(hostname));
2327 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002328 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002329 {
2330 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002331 memcpy(p, domain, strlen(domain));
2332 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002333 }
2334 if (null_term)
2335 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002336 }
2337 }
2338 }
2339 }
2340
Simon Kelley6b010842007-02-12 20:32:07 +00002341 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002342 {
Simon Kelley824af852008-02-12 20:43:05 +00002343 int optno = opt->opt;
2344
Simon Kelley7de060b2011-08-26 17:24:52 +01002345 /* netids match and not encapsulated? */
2346 if (!(opt->flags & DHOPT_TAGOK))
2347 continue;
2348
Simon Kelley6b010842007-02-12 20:32:07 +00002349 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002350 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002351 continue;
2352
2353 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002354 if (optno == OPTION_CLIENT_FQDN ||
2355 optno == OPTION_MAXMESSAGE ||
2356 optno == OPTION_OVERLOAD ||
2357 optno == OPTION_PAD ||
2358 optno == OPTION_END)
2359 continue;
2360
2361 if (optno == OPTION_SNAME && done_server)
2362 continue;
2363
2364 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002365 continue;
2366
Simon Kelley33820b72004-04-03 21:10:00 +01002367 /* For the options we have default values on
2368 dhc-option=<optionno> means "don't include this option"
2369 not "include a zero-length option" */
2370 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002371 (optno == OPTION_NETMASK ||
2372 optno == OPTION_BROADCAST ||
2373 optno == OPTION_ROUTER ||
2374 optno == OPTION_DNSSERVER ||
2375 optno == OPTION_DOMAINNAME ||
2376 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002377 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002378
2379 /* vendor-class comes from elsewhere for PXE */
2380 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2381 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002382
Simon Kelley7622fc02009-06-04 20:32:05 +01002383 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002384 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002385 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002386
Simon Kelley824af852008-02-12 20:43:05 +00002387 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002388 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002389 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002390 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2391
Simon Kelley6b010842007-02-12 20:32:07 +00002392 /* If we send a vendor-id, revisit which vendor-ops we consider
2393 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002394 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002395 {
2396 match_vendor_opts(p - 2, config_opts);
2397 done_vendor_class = 1;
2398 }
Simon Kelley6b010842007-02-12 20:32:07 +00002399 }
2400 }
2401
Simon Kelley73a08a22009-02-05 20:28:08 +00002402 /* Now send options to be encapsulated in arbitrary options,
2403 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002404 Also handle vendor-identifying vendor-encapsulated options,
2405 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002406 The may be more that one "outer" to do, so group
2407 all the options which match each outer in turn. */
2408 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002409 opt->flags &= ~DHOPT_ENCAP_DONE;
2410
2411 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002412 {
2413 int flags;
2414
2415 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2416 {
2417 int found = 0;
2418 struct dhcp_opt *o;
2419
2420 if (opt->flags & DHOPT_ENCAP_DONE)
2421 continue;
2422
2423 for (len = 0, o = config_opts; o; o = o->next)
2424 {
2425 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2426
2427 o->flags &= ~DHOPT_ENCAP_MATCH;
2428
2429 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2430 continue;
2431
2432 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002433 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002434 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2435 {
2436 o->flags |= DHOPT_ENCAP_MATCH;
2437 found = 1;
2438 len += do_opt(o, NULL, NULL, 0) + 2;
2439 }
2440 }
2441
2442 if (found)
2443 {
2444 if (flags & DHOPT_ENCAPSULATE)
2445 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2446 else if (len > 250)
2447 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2448 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2449 {
2450 int swap_ent = htonl(opt->u.encap);
2451 memcpy(p, &swap_ent, 4);
2452 p += 4;
2453 *(p++) = len;
2454 for (o = config_opts; o; o = o->next)
2455 if (o->flags & DHOPT_ENCAP_MATCH)
2456 {
2457 len = do_opt(o, p + 2, NULL, 0);
2458 *(p++) = o->opt;
2459 *(p++) = len;
2460 p += len;
2461 }
2462 }
2463 }
2464 }
2465 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002466
Simon Kelley7de060b2011-08-26 17:24:52 +01002467 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002468
Simon Kelley316e2732010-01-22 20:16:09 +00002469 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002470 {
2471 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002472 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002473 }
2474
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002475 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2476 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2477 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2478 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2479 /* If we send vendor encapsulated options, and haven't already sent option 60,
2480 echo back the value we got from the client. */
2481 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2482
Simon Kelley7622fc02009-06-04 20:32:05 +01002483 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002484 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002485 {
2486 mess->file[0] = f0;
2487 mess->sname[0] = s0;
2488 }
2489}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002490
Simon Kelley7622fc02009-06-04 20:32:05 +01002491#endif
2492
2493
2494
2495
2496
2497
2498