blob: 013a446bf26accc129df65dcf997f65eb1bc3bd1 [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,
37 int mac_len, char *interface, char *string, 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 }
358
Simon Kelley3be34542004-09-11 19:12:13 +0100359 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100360
Simon Kelleycdeda282006-03-16 20:16:06 +0000361 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
362 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100363
364 /* set "known" tag for known hosts */
365 if (config)
366 {
367 known_id.net = "known";
368 known_id.next = netid;
369 netid = &known_id;
370 }
Simon Kelley26128d22004-11-14 16:43:54 +0000371
Simon Kelley316e2732010-01-22 20:16:09 +0000372 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100373 {
374 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000375 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000376 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100377
378 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100379 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100380 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000381
Simon Kelley26128d22004-11-14 16:43:54 +0000382 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000383 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000384
385 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
386
387 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000388 {
389 hostname = config->hostname;
390 domain = config->domain;
391 }
392
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100393 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000394 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100395 struct dhcp_netid_list *list;
396
397 for (list = config->netid; list; list = list->next)
398 {
399 list->list->next = netid;
400 netid = list->list;
401 }
Simon Kelley26128d22004-11-14 16:43:54 +0000402 }
403
404 /* Match incoming filename field as a netid. */
405 if (mess->file[0])
406 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000407 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
408 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
409 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000410 id.next = netid;
411 netid = &id;
412 }
Simon Kelley6b010842007-02-12 20:32:07 +0000413
414 /* Add "bootp" as a tag to allow different options, address ranges etc
415 for BOOTP clients */
416 bootp_id.net = "bootp";
417 bootp_id.next = netid;
418 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000419
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100420 tagif_netid = run_tag_if(netid);
421
Simon Kelley26128d22004-11-14 16:43:54 +0000422 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100423 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100424 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000425
Simon Kelley3d8df262005-08-29 12:19:27 +0100426 if (!message)
427 {
Simon Kelley9009d742008-11-14 20:04:27 +0000428 int nailed = 0;
429
Simon Kelley3d8df262005-08-29 12:19:27 +0100430 if (have_config(config, CONFIG_ADDR))
431 {
Simon Kelley9009d742008-11-14 20:04:27 +0000432 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100433 logaddr = &config->addr;
434 mess->yiaddr = config->addr;
435 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000436 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100437 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000438 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000439 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100440 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100441 else
442 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000443 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100444 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000445 {
446 if (lease)
447 {
448 /* lease exists, wrong network. */
449 lease_prune(lease, now);
450 lease = NULL;
451 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100452 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000453 message = _("no address available");
454 }
455 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100456 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100457 }
458
Simon Kelley9009d742008-11-14 20:04:27 +0000459 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
460 message = _("wrong network");
461 else if (context->netid.net)
462 {
463 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100464 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100465 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100466
Simon Kelley4cb1b322012-02-06 14:30:41 +0000467 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100468
Simon Kelley9009d742008-11-14 20:04:27 +0000469 if (!message && !nailed)
470 {
471 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100472 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000473 break;
474 if (!id_list)
475 message = _("no address configured");
476 }
477
Simon Kelley7cebd202006-05-06 14:13:33 +0100478 if (!message &&
479 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000480 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000481 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000482
Simon Kelley3d8df262005-08-29 12:19:27 +0100483 if (!message)
484 {
485 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100486
Simon Kelleya9ab7322012-04-28 11:29:37 +0100487 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100488 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000489 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100490 /* infinite lease unless nailed in dhcp-host line. */
491 lease_set_expires(lease,
492 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
493 now);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000494 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100495
Simon Kelley7622fc02009-06-04 20:32:05 +0100496 clear_packet(mess, end);
Simon Kelley4d0f5b42012-09-05 23:29:30 +0100497 match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */
Simon Kelley9009d742008-11-14 20:04:27 +0000498 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelley4d0f5b42012-09-05 23:29:30 +0100499 netid, subnet_addr, 0, 0, -1, NULL, 0, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100500 }
501 }
502
Simon Kelley7622fc02009-06-04 20:32:05 +0100503 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000504
Simon Kelley7de060b2011-08-26 17:24:52 +0100505 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100506 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000507
Simon Kelley3d8df262005-08-29 12:19:27 +0100508 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
509 {
510 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
511 int len = option_len(opt);
512 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100513 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100514
Simon Kelley9fed0f72012-08-30 11:43:35 +0100515 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100516 len -= 3;
517 op += 3;
518 pp = op;
519
Simon Kelley9fed0f72012-08-30 11:43:35 +0100520 /* NB, the following always sets at least one bit */
521 if (option_bool(OPT_FQDN_UPDATE))
522 {
523 if (fqdn_flags & 0x01)
524 {
525 fqdn_flags |= 0x02; /* set O */
526 fqdn_flags &= ~0x01; /* clear S */
527 }
528 fqdn_flags |= 0x08; /* set N */
529 }
530 else
531 {
532 if (!(fqdn_flags & 0x01))
533 fqdn_flags |= 0x03; /* set S and O */
534 fqdn_flags &= ~0x08; /* clear N */
535 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100536
537 if (fqdn_flags & 0x04)
538 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
539 {
540 memcpy(pq, op+1, *op);
541 pq += *op;
542 op += (*op)+1;
543 *(pq++) = '.';
544 }
545 else
546 {
547 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000548 if (len > 0 && op[len-1] == 0)
549 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100550 pq += len + 1;
551 }
552
553 if (pq != daemon->dhcp_buff)
554 pq--;
555
556 *pq = 0;
557
Simon Kelley1f15b812009-10-13 17:49:32 +0100558 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100559 offer_hostname = client_hostname = daemon->dhcp_buff;
560 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000561 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000562 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000563 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100564 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000565 /* Microsoft clients are broken, and need zero-terminated strings
566 in options. We detect this state here, and do the same in
567 any options we send */
568 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
569 borken_opt = 1;
570 else
571 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100572 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100573 client_hostname = daemon->dhcp_buff;
574 }
575
Simon Kelley28866e92011-02-14 20:19:14 +0000576 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100577 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
578
Simon Kelley3d8df262005-08-29 12:19:27 +0100579 if (have_config(config, CONFIG_NAME))
580 {
581 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000582 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000583 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000584 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100585 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000586 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100587 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100588 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100589 {
Simon Kelley9009d742008-11-14 20:04:27 +0000590 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100591
592 if (strlen(client_hostname) != 0)
593 {
594 hostname = client_hostname;
595 if (!config)
596 {
597 /* Search again now we have a hostname.
598 Only accept configs without CLID and HWADDR here, (they won't match)
599 to avoid impersonation by name. */
600 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
601 mess->chaddr, mess->hlen,
602 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000603 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000604 {
605 config = new;
606 /* set "known" tag for known hosts */
607 known_id.net = "known";
608 known_id.next = netid;
609 netid = &known_id;
610 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100611 }
612 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000613 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100614
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100615 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100616 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100617 struct dhcp_netid_list *list;
618
619 for (list = config->netid; list; list = list->next)
620 {
621 list->list->next = netid;
622 netid = list->list;
623 }
Simon Kelleya2226412004-05-13 20:27:08 +0100624 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100625
Simon Kelley73a08a22009-02-05 20:28:08 +0000626 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
627 Otherwise assume the option is an array, and look for a matching element.
Simon Kelley316e2732010-01-22 20:16:09 +0000628 If no data given, existance of the option is enough. This code handles
629 rfc3925 V-I classes too. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000630 for (o = daemon->dhcp_match; o; o = o->next)
631 {
Simon Kelley316e2732010-01-22 20:16:09 +0000632 unsigned int len, elen, match = 0;
633 size_t offset, o2;
Simon Kelley73a08a22009-02-05 20:28:08 +0000634
Simon Kelley316e2732010-01-22 20:16:09 +0000635 if (o->flags & DHOPT_RFC3925)
636 {
637 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
638 continue;
639
640 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
641 {
642 len = option_uint(opt, offset + 4 , 1);
643 /* Need to take care that bad data can't run us off the end of the packet */
644 if ((offset + len + 5 <= (option_len(opt))) &&
645 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
646 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
647 {
648 elen = option_uint(opt, o2, 1);
649 if ((o2 + elen + 1 <= option_len(opt)) &&
650 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
651 break;
652 }
653 if (match)
654 break;
655 }
656 }
657 else
658 {
659 if (!(opt = option_find(mess, sz, o->opt, 1)))
660 continue;
661
662 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
663 }
664
665 if (match)
Simon Kelley73a08a22009-02-05 20:28:08 +0000666 {
667 o->netid->next = netid;
668 netid = o->netid;
669 }
670 }
671
Simon Kelley26128d22004-11-14 16:43:54 +0000672 /* user-class options are, according to RFC3004, supposed to contain
673 a set of counted strings. Here we check that this is so (by seeing
674 if the counts are consistent with the overall option length) and if
675 so zero the counts so that we don't get spurious matches between
676 the vendor string and the counts. If the lengths don't add up, we
677 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100678 and just do the substring match. dhclient provides these broken options.
679 The code, later, which sends user-class data to the lease-change script
680 relies on the transformation done here.
681 */
Simon Kelleya2226412004-05-13 20:27:08 +0100682
Simon Kelleybb01cb92004-12-13 20:56:23 +0000683 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100684 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100685 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000686 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000687 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
688 if (j == option_len(opt))
689 for (j = 0; j < option_len(opt); j = tmp)
690 {
691 tmp = j + ucp[j] + 1;
692 ucp[j] = 0;
693 }
Simon Kelleya2226412004-05-13 20:27:08 +0100694 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000695
Simon Kelley26128d22004-11-14 16:43:54 +0000696 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100697 {
698 int mopt;
699
700 if (vendor->match_type == MATCH_VENDOR)
701 mopt = OPTION_VENDOR_ID;
702 else if (vendor->match_type == MATCH_USER)
703 mopt = OPTION_USER_CLASS;
704 else
705 continue;
706
707 if ((opt = option_find(mess, sz, mopt, 1)))
708 {
709 int i;
710 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100711 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100712 {
713 vendor->netid.next = netid;
714 netid = &vendor->netid;
715 break;
716 }
717 }
718 }
719
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100720 /* mark vendor-encapsulated options which match the client-supplied vendor class,
721 save client-supplied vendor class */
722 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
723 {
724 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
725 vendor_class_len = option_len(opt);
726 }
727 match_vendor_opts(opt, daemon->dhcp_opts);
728
Simon Kelley28866e92011-02-14 20:19:14 +0000729 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100730 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100731 if (sanitise(opt, daemon->namebuff))
732 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100733 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100734 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100735 }
736
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100737 tagif_netid = run_tag_if(netid);
738
Simon Kelley26128d22004-11-14 16:43:54 +0000739 /* if all the netids in the ignore list are present, ignore this client */
740 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100741 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000742 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100743
744 /* If configured, we can override the server-id to be the address of the relay,
745 so that all traffic goes via the relay and can pick up agent-id info. This can be
746 configured for all relays, or by address. */
747 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
748 {
749 if (!daemon->override_relays)
750 override = mess->giaddr;
751 else
752 {
753 struct addr_list *l;
754 for (l = daemon->override_relays; l; l = l->next)
755 if (l->addr.s_addr == mess->giaddr.s_addr)
756 break;
757 if (l)
758 override = mess->giaddr;
759 }
760 }
761
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100762 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
763 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000764 clid = NULL;
765
Simon Kelley7622fc02009-06-04 20:32:05 +0100766 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100767 if (daemon->enable_pxe &&
768 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100769 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
770 {
771 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
772 {
773 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
774 uuid = pxe_uuid;
775 }
776
777 /* Check if this is really a PXE bootserver request, and handle specially if so. */
778 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
779 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
780 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
781 {
782 struct pxe_service *service;
783 int type = option_uint(opt, 0, 2);
784 int layer = option_uint(opt, 2, 2);
785 unsigned char save71[4];
786 struct dhcp_opt opt71;
787
Simon Kelley1f15b812009-10-13 17:49:32 +0100788 if (ignore)
789 return 0;
790
Simon Kelley7622fc02009-06-04 20:32:05 +0100791 if (layer & 0x8000)
792 {
793 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
794 return 0;
795 }
796
797 memcpy(save71, option_ptr(opt, 0), 4);
798
799 for (service = daemon->pxe_services; service; service = service->next)
800 if (service->type == type)
801 break;
802
803 if (!service || !service->basename)
804 return 0;
805
806 clear_packet(mess, end);
807
808 mess->yiaddr = mess->ciaddr;
809 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000810 if (service->sname)
811 mess->siaddr = a_record_from_hosts(service->sname, now);
812 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100813 mess->siaddr = service->server;
814 else
815 mess->siaddr = context->local;
816
817 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
818 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
819 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
820 pxe_misc(mess, end, uuid);
821
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100822 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100823 opt71.val = save71;
824 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
825 opt71.len = 4;
826 opt71.flags = DHOPT_VENDOR_MATCH;
827 opt71.netid = NULL;
828 opt71.next = daemon->dhcp_opts;
829 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
830
831 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000832 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100833 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100834 }
835
836 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
837 {
838 pxearch = option_uint(opt, 0, 2);
839
Simon Kelley316e2732010-01-22 20:16:09 +0000840 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000841 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100842 {
Simon Kelley28866e92011-02-14 20:19:14 +0000843 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100844
Simon Kelley28866e92011-02-14 20:19:14 +0000845 for (tmp = context; tmp; tmp = tmp->current)
846 if ((tmp->flags & CONTEXT_PROXY) &&
847 match_netid(tmp->filter, tagif_netid, 1))
848 break;
849
850 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100851 {
Simon Kelley28866e92011-02-14 20:19:14 +0000852 struct dhcp_boot *boot = find_boot(tagif_netid);
853
854 mess->yiaddr.s_addr = 0;
855 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
856 {
857 mess->ciaddr.s_addr = 0;
858 mess->flags |= htons(0x8000); /* broadcast */
859 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100860
Simon Kelley28866e92011-02-14 20:19:14 +0000861 clear_packet(mess, end);
862
863 /* Provide the bootfile here, for gPXE, and in case we have no menu items
864 and set discovery_control = 8 */
865 if (boot)
866 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100867 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000868 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100869 else if (boot->tftp_sname)
870 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000871
872 if (boot->file)
873 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
874 }
875
876 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
877 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
878 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
879 pxe_misc(mess, end, uuid);
880 prune_vendor_opts(tagif_netid);
Simon Kelley751d6f42012-02-10 15:24:51 +0000881 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 +0000882
883 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000884 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100885 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100886 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100887 }
888 }
889 }
890
891 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000892 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100893 return 0;
894
Simon Kelleybb01cb92004-12-13 20:56:23 +0000895 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100896 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100897 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100898 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000899 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100900 }
901
Simon Kelley3be34542004-09-11 19:12:13 +0100902 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000903 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000904 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000905 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000906 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000907 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100908
Simon Kelley44a2a312004-03-10 20:04:35 +0000909 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100910 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000911
Simon Kelleybb01cb92004-12-13 20:56:23 +0000912 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000913 return 0;
914
Simon Kelley7622fc02009-06-04 20:32:05 +0100915 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000916
917 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
918 lease_prune(lease, now);
919
Simon Kelley33820b72004-04-03 21:10:00 +0100920 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000921 config->addr.s_addr == option_addr(opt).s_addr)
922 {
Simon Kelley849a8352006-06-09 21:02:31 +0100923 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100924 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100925 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100926 config->flags |= CONFIG_DECLINED;
927 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000928 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100929 else
930 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100931 for (; context; context = context->current)
932 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000933
934 return 0;
935
936 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100937 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100938 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000939 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000940 return 0;
941
Simon Kelley44a2a312004-03-10 20:04:35 +0000942 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
943 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100944 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000945 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100946
Simon Kelley7622fc02009-06-04 20:32:05 +0100947 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000948
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000949 return 0;
950
951 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100952 if (ignore || have_config(config, CONFIG_DISABLE))
953 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000954 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100955 opt = NULL;
956 }
957 else
958 {
959 struct in_addr addr, conf;
960
Simon Kelley1a6bca82008-07-11 11:11:42 +0100961 addr.s_addr = conf.s_addr = 0;
962
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100963 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
964 addr = option_addr(opt);
965
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100966 if (have_config(config, CONFIG_ADDR))
967 {
Simon Kelley849a8352006-06-09 21:02:31 +0100968 char *addrs = inet_ntoa(config->addr);
969
Simon Kelley9009d742008-11-14 20:04:27 +0000970 if ((ltmp = lease_find_by_addr(config->addr)) &&
971 ltmp != lease &&
972 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000973 {
974 int len;
975 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
976 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100977 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100978 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000979 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100980 else
981 {
982 struct dhcp_context *tmp;
983 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100984 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100985 break;
986 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100987 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 +0100988 else if (have_config(config, CONFIG_DECLINED) &&
989 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100990 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100991 else
992 conf = config->addr;
993 }
994 }
995
996 if (conf.s_addr)
997 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100998 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100999 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +01001000 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001001 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001002 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001003 !config_find_by_address(daemon->dhcp_conf, addr))
1004 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001005 else if (emac_len == 0)
1006 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001007 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001008 message = _("no address available");
1009 }
1010
Simon Kelley7622fc02009-06-04 20:32:05 +01001011 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001012
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001013 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001014 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001015
Simon Kelleycdeda282006-03-16 20:16:06 +00001016 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001017 {
1018 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001019 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001020 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001021
Simon Kelley4cb1b322012-02-06 14:30:41 +00001022 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001023
1024 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1025
Simon Kelley824af852008-02-12 20:43:05 +00001026 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001027 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001028 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001029 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001030 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001031 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001032 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001033 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001034 option_put(mess, end, OPTION_T1, 4, (time/2));
1035 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001036 }
Simon Kelley9009d742008-11-14 20:04:27 +00001037 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001038 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001039
Simon Kelley7de060b2011-08-26 17:24:52 +01001040 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001041
1042 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001043 if (ignore || have_config(config, CONFIG_DISABLE))
1044 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001045 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001046 {
1047 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001048 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001049
Simon Kelley4011c4e2006-10-28 16:26:19 +01001050 /* send vendor and user class info for new or recreated lease */
1051 do_classes = 1;
1052
Simon Kelleybb01cb92004-12-13 20:56:23 +00001053 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001054 {
Simon Kelley3be34542004-09-11 19:12:13 +01001055 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001056 selecting = 1;
1057
Simon Kelley1a6bca82008-07-11 11:11:42 +01001058 if (override.s_addr != 0)
1059 {
1060 if (option_addr(opt).s_addr != override.s_addr)
1061 return 0;
1062 }
Simon Kelley9009d742008-11-14 20:04:27 +00001063 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001064 {
1065 for (; context; context = context->current)
1066 if (context->local.s_addr == option_addr(opt).s_addr)
1067 break;
1068
1069 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001070 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001071 /* Handle very strange configs where clients have more than one route to the server.
1072 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1073 Have to set override to make sure we echo back the correct server-id */
1074 struct irec *intr;
1075
1076 enumerate_interfaces();
1077
1078 for (intr = daemon->interfaces; intr; intr = intr->next)
1079 if (intr->addr.sa.sa_family == AF_INET &&
1080 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1081 intr->tftp_ok)
1082 break;
1083
1084 if (intr)
1085 override = intr->addr.in.sin_addr;
1086 else
1087 {
1088 /* In auth mode, a REQUEST sent to the wrong server
1089 should be faulted, so that the client establishes
1090 communication with us, otherwise, silently ignore. */
1091 if (!option_bool(OPT_AUTHORITATIVE))
1092 return 0;
1093 message = _("wrong server-ID");
1094 }
Simon Kelley9009d742008-11-14 20:04:27 +00001095 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001096 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001097
Simon Kelley3be34542004-09-11 19:12:13 +01001098 /* If a lease exists for this host and another address, squash it. */
1099 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1100 {
1101 lease_prune(lease, now);
1102 lease = NULL;
1103 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001104 }
Simon Kelley3be34542004-09-11 19:12:13 +01001105 else
1106 {
1107 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001108 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001109 return 0;
1110
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001111 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001112 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001113 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001114 }
1115 else
1116 {
1117 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001118 /* Check existing lease for this address.
1119 We allow it to be missing if dhcp-authoritative mode
1120 as long as we can allocate the lease now - checked below.
1121 This makes for a smooth recovery from a lost lease DB */
1122 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001123 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001124 {
Simon Kelley28866e92011-02-14 20:19:14 +00001125 /* A client rebinding will broadcast the request, so we may see it even
1126 if the lease is held by another server. Just ignore it in that case.
1127 If the request is unicast to us, then somethings wrong, NAK */
1128 if (!unicast_dest)
1129 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001130 message = _("lease not found");
1131 /* ensure we broadcast NAK */
1132 unicast_dest = 0;
1133 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001134
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001135 /* desynchronise renewals */
1136 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001137 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001138 }
1139
Simon Kelley7622fc02009-06-04 20:32:05 +01001140 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001141
Simon Kelleydfa666f2004-08-02 18:27:27 +01001142 if (!message)
1143 {
1144 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001145 struct dhcp_context *tmp = NULL;
1146
1147 if (have_config(config, CONFIG_ADDR))
1148 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001149 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001150 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001151
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001152 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001153 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001154 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001155 message = _("wrong network");
1156 /* ensure we broadcast NAK */
1157 unicast_dest = 0;
1158 }
1159
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001160 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001161 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001162 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001163 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001164
Simon Kelleydfa666f2004-08-02 18:27:27 +01001165 /* Check if a new static address has been configured. Be very sure that
1166 when the client does DISCOVER, it will get the static address, otherwise
1167 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001168 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001169 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001170 (!have_config(config, CONFIG_DECLINED) ||
1171 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001172 config->addr.s_addr != mess->yiaddr.s_addr &&
1173 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001174 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001175
1176 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001177 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001178 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001179
Simon Kelley9009d742008-11-14 20:04:27 +00001180 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1181 {
1182 /* If a host is configured with more than one MAC address, it's OK to 'nix
1183 a lease from one of it's MACs to give the address to another. */
1184 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1185 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001186 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001187 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1188 inet_ntoa(ltmp->addr));
1189 lease = ltmp;
1190 }
Simon Kelley16972692006-10-16 20:04:18 +01001191 else
Simon Kelley9009d742008-11-14 20:04:27 +00001192 message = _("address in use");
1193 }
1194
1195 if (!message)
1196 {
1197 if (emac_len == 0)
1198 message = _("no unique-id");
1199
1200 else if (!lease)
1201 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001202 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001203 do_classes = 1;
1204 else
1205 message = _("no leases left");
1206 }
Simon Kelley16972692006-10-16 20:04:18 +01001207 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001208 }
Simon Kelley16972692006-10-16 20:04:18 +01001209
Simon Kelley44a2a312004-03-10 20:04:35 +00001210 if (message)
1211 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001212 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001213
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001214 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001215 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001216 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001217 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001218 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001219 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1220 a distant subnet which unicast a REQ to us won't work. */
1221 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1222 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1223 {
1224 mess->flags |= htons(0x8000); /* broadcast */
1225 mess->ciaddr.s_addr = 0;
1226 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001227 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001228 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001229 {
Simon Kelley316e2732010-01-22 20:16:09 +00001230 if (context->netid.net)
1231 {
1232 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001233 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001234 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001235
Simon Kelley4cb1b322012-02-06 14:30:41 +00001236 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001237
Simon Kelleydcffad22012-04-24 15:25:18 +01001238 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001239 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001240 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001241 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001242
Simon Kelleydcffad22012-04-24 15:25:18 +01001243#ifdef HAVE_SCRIPT
1244 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001245 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001246 struct dhcp_netid *n;
1247
1248 if (mess->giaddr.s_addr)
1249 lease->giaddr = mess->giaddr;
1250
1251 free(lease->extradata);
1252 lease->extradata = NULL;
1253 lease->extradata_size = lease->extradata_len = 0;
1254
1255 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1256 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1257 add_extradata_opt(lease, oui);
1258 add_extradata_opt(lease, serial);
1259 add_extradata_opt(lease, class);
Simon Kelleydd1721c2013-02-18 21:04:04 +00001260
1261 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
1262 {
1263 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_CIRCUIT_ID, 1));
1264 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBSCR_ID, 1));
1265 add_extradata_opt(lease, option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, 1));
1266 }
1267 else
1268 {
1269 add_extradata_opt(lease, NULL);
1270 add_extradata_opt(lease, NULL);
1271 add_extradata_opt(lease, NULL);
1272 }
1273
Simon Kelleydcffad22012-04-24 15:25:18 +01001274 /* space-concat tag set */
1275 if (!tagif_netid)
1276 add_extradata_opt(lease, NULL);
1277 else
1278 for (n = tagif_netid; n; n = n->next)
1279 {
1280 struct dhcp_netid *n1;
1281 /* kill dupes */
1282 for (n1 = n->next; n1; n1 = n1->next)
1283 if (strcmp(n->net, n1->net) == 0)
1284 break;
1285 if (!n1)
1286 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1287 }
1288
1289 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1290 {
1291 int len = option_len(opt);
1292 unsigned char *ucp = option_ptr(opt, 0);
1293 /* If the user-class option started as counted strings, the first byte will be zero. */
1294 if (len != 0 && ucp[0] == 0)
1295 ucp++, len--;
1296 lease_add_extradata(lease, ucp, len, 0);
1297 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001298 }
Simon Kelley316e2732010-01-22 20:16:09 +00001299#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001300 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001301
1302 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1303 {
1304 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001305 hostname = client_hostname;
1306 hostname_auth = 1;
1307 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001308
Simon Kelley824af852008-02-12 20:43:05 +00001309 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001310 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001311
Simon Kelley832af0b2007-01-21 20:01:28 +00001312 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1313 if (!hostname_auth)
1314 {
1315 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001316 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001317 break;
1318 if (id_list)
1319 hostname = NULL;
1320 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001321
1322 /* Last ditch, if configured, generate hostname from mac address */
1323 if (!hostname && emac_len != 0)
1324 {
1325 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1326 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1327 break;
1328 if (id_list)
1329 {
1330 int i;
1331
1332 hostname = daemon->dhcp_buff;
1333 /* buffer is 256 bytes, 3 bytes per octet */
1334 for (i = 0; (i < emac_len) && (i < 80); i++)
1335 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1336 hostname = daemon->dhcp_buff;
1337 }
1338 }
1339
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001340 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001341 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001342
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001343 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001344 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001345
1346 if (override.s_addr != 0)
1347 lease->override = override;
1348 else
1349 override = lease->override;
1350
Simon Kelley7622fc02009-06-04 20:32:05 +01001351 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001352
Simon Kelley7622fc02009-06-04 20:32:05 +01001353 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001354 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001355 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001356 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001357 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001358 {
Simon Kelley59353a62004-11-21 19:34:28 +00001359 while (fuzz > (time/16))
1360 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001361 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1362 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001363 }
Simon Kelley9009d742008-11-14 20:04:27 +00001364 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001365 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001366 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001367
Simon Kelley7de060b2011-08-26 17:24:52 +01001368 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001369
1370 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001371 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001372 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001373
Simon Kelley7622fc02009-06-04 20:32:05 +01001374 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001375
Simon Kelley73a08a22009-02-05 20:28:08 +00001376 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001377 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001378
1379 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001380 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001381
Simon Kelley5aabfc72007-08-29 11:24:47 +01001382 /* Find a least based on IP address if we didn't
1383 get one from MAC address/client-d */
1384 if (!lease &&
1385 (lease = lease_find_by_addr(mess->ciaddr)) &&
1386 lease->hostname)
1387 hostname = lease->hostname;
1388
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001389 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1390 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001391
Simon Kelley73a08a22009-02-05 20:28:08 +00001392 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001393 {
1394 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001395 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001396 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001397
Simon Kelley4cb1b322012-02-06 14:30:41 +00001398 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001399
1400 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001401
Simon Kelley3927da42008-07-20 15:10:39 +01001402 if (lease)
1403 {
Simon Kelleyd1a59752012-11-05 16:50:30 +00001404 lease_set_interface(lease, int_index, now);
Simon Kelley3927da42008-07-20 15:10:39 +01001405 if (override.s_addr != 0)
1406 lease->override = override;
1407 else
1408 override = lease->override;
1409 }
1410
Simon Kelley7622fc02009-06-04 20:32:05 +01001411 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001412 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001413 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelleyaa63a212013-04-22 15:01:52 +01001414
1415 /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but
1416 we supply a utility which makes DHCPINFORM requests to get this information.
1417 Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
1418 which won't be true for ordinary clients, but will be true for the
1419 dhcp_lease_time utility. */
1420 if (lease && in_list(req_options, OPTION_LEASE_TIME))
1421 {
1422 if (lease->expires == 0)
1423 time = 0xffffffff;
1424 else
1425 time = (unsigned int)difftime(lease->expires, now);
1426 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
1427 }
1428
Simon Kelley9009d742008-11-14 20:04:27 +00001429 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001430 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001431
Simon Kelley5aabfc72007-08-29 11:24:47 +01001432 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001433 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001434 }
1435
1436 return 0;
1437}
1438
Simon Kelley6b010842007-02-12 20:32:07 +00001439/* find a good value to use as MAC address for logging and address-allocation hashing.
1440 This is normally just the chaddr field from the DHCP packet,
1441 but eg Firewire will have hlen == 0 and use the client-id instead.
1442 This could be anything, but will normally be EUI64 for Firewire.
1443 We assume that if the first byte of the client-id equals the htype byte
1444 then the client-id is using the usual encoding and use the rest of the
1445 client-id: if not we can use the whole client-id. This should give
1446 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001447unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001448 int clid_len, unsigned char *clid, int *len_out)
1449{
1450 if (hwlen == 0 && clid && clid_len > 3)
1451 {
1452 if (clid[0] == hwtype)
1453 {
1454 *len_out = clid_len - 1 ;
1455 return clid + 1;
1456 }
1457
1458#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1459 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1460 {
1461 *len_out = clid_len - 1 ;
1462 return clid + 1;
1463 }
1464#endif
1465
1466 *len_out = clid_len;
1467 return clid;
1468 }
1469
1470 *len_out = hwlen;
1471 return hwaddr;
1472}
1473
Simon Kelley824af852008-02-12 20:43:05 +00001474static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001475{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001476 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001477
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001478 if (opt)
1479 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001480 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001481 if (req_time < 120 )
1482 req_time = 120; /* sanity */
1483 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1484 time = req_time;
1485 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001486
1487 return time;
1488}
1489
Simon Kelley73a08a22009-02-05 20:28:08 +00001490static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001491{
Simon Kelley73a08a22009-02-05 20:28:08 +00001492 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001493 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001494 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001495 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001496 else
1497 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001498}
1499
Simon Kelleyf2621c72007-04-29 19:47:21 +01001500static int sanitise(unsigned char *opt, char *buf)
1501{
1502 char *p;
1503 int i;
1504
1505 *buf = 0;
1506
1507 if (!opt)
1508 return 0;
1509
Simon Kelley1a6bca82008-07-11 11:11:42 +01001510 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001511
1512 for (i = option_len(opt); i > 0; i--)
1513 {
1514 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001515 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001516 *buf++ = c;
1517 }
1518 *buf = 0; /* add terminator */
1519
1520 return 1;
1521}
1522
Simon Kelley316e2732010-01-22 20:16:09 +00001523#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001524static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1525{
1526 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001527 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001528 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001529 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001530}
1531#endif
1532
Simon Kelley5aabfc72007-08-29 11:24:47 +01001533static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001534 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001535{
Simon Kelley16972692006-10-16 20:04:18 +01001536 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001537
Simon Kelley16972692006-10-16 20:04:18 +01001538 /* addr may be misaligned */
1539 if (addr)
1540 memcpy(&a, addr, sizeof(a));
1541
Simon Kelley7622fc02009-06-04 20:32:05 +01001542 print_mac(daemon->namebuff, ext_mac, mac_len);
1543
Simon Kelley28866e92011-02-14 20:19:14 +00001544 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001545 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1546 ntohl(xid),
1547 type,
1548 interface,
1549 addr ? inet_ntoa(a) : "",
1550 addr ? " " : "",
1551 daemon->namebuff,
1552 string ? string : "");
1553 else
1554 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1555 type,
1556 interface,
1557 addr ? inet_ntoa(a) : "",
1558 addr ? " " : "",
1559 daemon->namebuff,
1560 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001561}
1562
Simon Kelley7622fc02009-06-04 20:32:05 +01001563static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001564{
1565 while (*start != OPTION_END)
1566 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001567 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 +01001568
Simon Kelley4cb1b322012-02-06 14:30:41 +00001569 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1570 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001571 start += start[1] + 2;
1572 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001573}
1574
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001575static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001576{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001577 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001578 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001579 if (p > end)
1580 return NULL;
1581 else if (*p == OPTION_END)
1582 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001583 else if (*p == OPTION_PAD)
1584 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001585 else
1586 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001587 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001588 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001589 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001590 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001591 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001592 return NULL; /* malformed packet */
1593 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001594 return p;
1595 p += opt_len + 2;
1596 }
1597 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001598}
1599
Simon Kelleycdeda282006-03-16 20:16:06 +00001600static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001601{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001602 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001603
Simon Kelley3be34542004-09-11 19:12:13 +01001604 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001605 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1606 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001607
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001608 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001609 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001610 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001611
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001612 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001613 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001614 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1615 return ret;
1616
1617 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001618 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001619 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1620 return ret;
1621
1622 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001623}
1624
Simon Kelley4cb1b322012-02-06 14:30:41 +00001625static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001626{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001627 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001628 /* struct in_addr is network byte order */
1629 struct in_addr ret;
1630
Simon Kelley4cb1b322012-02-06 14:30:41 +00001631 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001632
1633 return ret;
1634}
1635
Simon Kelley7622fc02009-06-04 20:32:05 +01001636static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001637{
1638 /* this worries about unaligned data and byte order */
1639 unsigned int ret = 0;
1640 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001641 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001642
1643 for (i = 0; i < size; i++)
1644 ret = (ret << 8) | *p++;
1645
1646 return ret;
1647}
1648
1649static unsigned char *dhcp_skip_opts(unsigned char *start)
1650{
1651 while (*start != 0)
1652 start += start[1] + 2;
1653 return start;
1654}
1655
1656/* only for use when building packet: doesn't check for bad data. */
1657static unsigned char *find_overload(struct dhcp_packet *mess)
1658{
1659 unsigned char *p = &mess->options[0] + sizeof(u32);
1660
1661 while (*p != 0)
1662 {
1663 if (*p == OPTION_OVERLOAD)
1664 return p;
1665 p += p[1] + 2;
1666 }
1667 return NULL;
1668}
1669
Simon Kelley7de060b2011-08-26 17:24:52 +01001670static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1671{
1672 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1673 unsigned char *overload;
1674 size_t ret;
1675
1676 /* move agent_id back down to the end of the packet */
1677 if (agent_id)
1678 {
1679 memmove(p, agent_id, real_end - agent_id);
1680 p += real_end - agent_id;
1681 memset(p, 0, real_end - p); /* in case of overlap */
1682 }
1683
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001684 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001685 overload = find_overload(mess);
1686
1687 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001688 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001689 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001690 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001691 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001692 }
Simon Kelley28866e92011-02-14 20:19:14 +00001693 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001694 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1695
1696 if (overload && (option_uint(overload, 0, 1) & 2))
1697 {
1698 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001699 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001700 log_options(mess->sname, mess->xid);
1701 }
Simon Kelley28866e92011-02-14 20:19:14 +00001702 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001703 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1704
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001705
1706 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001707
Simon Kelley28866e92011-02-14 20:19:14 +00001708 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001709 {
1710 if (mess->siaddr.s_addr != 0)
1711 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1712
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001713 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1714 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1715
Simon Kelley7622fc02009-06-04 20:32:05 +01001716 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1717 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001718
1719 ret = (size_t)(p - (unsigned char *)mess);
1720
1721 if (ret < MIN_PACKETSZ)
1722 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001723
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001724 return ret;
1725}
1726
1727static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1728{
1729 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1730
1731 if (p + len + 3 >= end)
1732 /* not enough space in options area, try and use overload, if poss */
1733 {
1734 unsigned char *overload;
1735
1736 if (!(overload = find_overload(mess)) &&
1737 (mess->file[0] == 0 || mess->sname[0] == 0))
1738 {
1739 /* attempt to overload fname and sname areas, we've reserved space for the
1740 overflow option previuously. */
1741 overload = p;
1742 *(p++) = OPTION_OVERLOAD;
1743 *(p++) = 1;
1744 }
1745
1746 p = NULL;
1747
1748 /* using filename field ? */
1749 if (overload)
1750 {
1751 if (mess->file[0] == 0)
1752 overload[2] |= 1;
1753
1754 if (overload[2] & 1)
1755 {
1756 p = dhcp_skip_opts(mess->file);
1757 if (p + len + 3 >= mess->file + sizeof(mess->file))
1758 p = NULL;
1759 }
1760
1761 if (!p)
1762 {
1763 /* try to bring sname into play (it may be already) */
1764 if (mess->sname[0] == 0)
1765 overload[2] |= 2;
1766
1767 if (overload[2] & 2)
1768 {
1769 p = dhcp_skip_opts(mess->sname);
Simon Kelleyffa3d7d2013-02-04 21:35:43 +00001770 if (p + len + 3 >= mess->sname + sizeof(mess->sname))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001771 p = NULL;
1772 }
1773 }
1774 }
1775
1776 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001777 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 +00001778 }
1779
1780 if (p)
1781 {
1782 *(p++) = opt;
1783 *(p++) = len;
1784 }
1785
1786 return p;
1787}
1788
1789static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1790{
1791 int i;
1792 unsigned char *p = free_space(mess, end, opt, len);
1793
1794 if (p)
1795 for (i = 0; i < len; i++)
1796 *(p++) = val >> (8 * (len - (i + 1)));
1797}
1798
1799static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1800 char *string, int null_term)
1801{
1802 unsigned char *p;
1803 size_t len = strlen(string);
1804
1805 if (null_term && len != 255)
1806 len++;
1807
1808 if ((p = free_space(mess, end, opt, len)))
1809 memcpy(p, string, len);
1810}
1811
1812/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001813static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001814{
1815 int len = opt->len;
1816
1817 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1818 len++;
1819
1820 if (p && len != 0)
1821 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001822 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001823 {
1824 int j;
1825 struct in_addr *a = (struct in_addr *)opt->val;
1826 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1827 {
1828 /* zero means "self" (but not in vendorclass options.) */
1829 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001830 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001831 else
1832 memcpy(p, a, INADDRSZ);
1833 p += INADDRSZ;
1834 }
1835 }
1836 else
1837 memcpy(p, opt->val, len);
1838 }
1839 return len;
1840}
Simon Kelley7622fc02009-06-04 20:32:05 +01001841
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001842static int in_list(unsigned char *list, int opt)
1843{
1844 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001845
1846 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001847 if (!list)
1848 return 1;
1849
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001850 for (i = 0; list[i] != OPTION_END; i++)
1851 if (opt == list[i])
1852 return 1;
1853
1854 return 0;
1855}
1856
Simon Kelley7de060b2011-08-26 17:24:52 +01001857static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001858{
Simon Kelley7de060b2011-08-26 17:24:52 +01001859 struct dhcp_opt *opts;
1860
1861 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1862 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1863 return opts;
1864
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001865 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001866}
1867
Simon Kelley6b010842007-02-12 20:32:07 +00001868/* mark vendor-encapsulated options which match the client-supplied or
1869 config-supplied vendor class */
1870static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1871{
1872 for (; dopt; dopt = dopt->next)
1873 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001874 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001875 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001876 {
1877 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001878 if (dopt->u.vendor_class)
1879 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001880 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001881 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001882 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001883 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001884 break;
1885 }
1886 }
1887 }
1888}
1889
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001890static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1891 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001892{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001893 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001894 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001895 unsigned char *p;
1896
1897 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001898 for (enc_len = 0, start = opt; opt; opt = opt->next)
1899 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001900 {
1901 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001902 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001903 if (enc_len + new <= 255)
1904 enc_len += new;
1905 else
1906 {
1907 p = free_space(mess, end, encap, enc_len);
1908 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001909 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001910 {
1911 len = do_opt(start, p + 2, NULL, null_term);
1912 *(p++) = start->opt;
1913 *(p++) = len;
1914 p += len;
1915 }
1916 enc_len = new;
1917 start = opt;
1918 }
1919 }
1920
1921 if (enc_len != 0 &&
1922 (p = free_space(mess, end, encap, enc_len + 1)))
1923 {
1924 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001925 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001926 {
1927 len = do_opt(start, p + 2, NULL, null_term);
1928 *(p++) = start->opt;
1929 *(p++) = len;
1930 p += len;
1931 }
1932 *p = OPTION_END;
1933 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001934
1935 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00001936}
1937
Simon Kelley7622fc02009-06-04 20:32:05 +01001938static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01001939{
Simon Kelley7622fc02009-06-04 20:32:05 +01001940 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01001941
Simon Kelley7622fc02009-06-04 20:32:05 +01001942 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1943 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1944 memcpy(p, uuid, 17);
1945}
1946
1947static int prune_vendor_opts(struct dhcp_netid *netid)
1948{
1949 int force = 0;
1950 struct dhcp_opt *opt;
1951
1952 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1953 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1954 if (opt->flags & DHOPT_VENDOR_MATCH)
1955 {
1956 if (!match_netid(opt->netid, netid, 1))
1957 opt->flags &= ~DHOPT_VENDOR_MATCH;
1958 else if (opt->flags & DHOPT_FORCE)
1959 force = 1;
1960 }
1961 return force;
1962}
1963
Simon Kelley751d6f42012-02-10 15:24:51 +00001964static 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 +01001965{
1966#define NUM_OPTS 4
1967
1968 unsigned char *p, *q;
1969 struct pxe_service *service;
1970 static struct dhcp_opt *o, *ret;
1971 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001972 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01001973
1974 /* We pass back references to these, hence they are declared static */
1975 static unsigned char discovery_control;
1976 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1977 static struct dhcp_opt *fake_opts = NULL;
1978
Simon Kelley316e2732010-01-22 20:16:09 +00001979 /* Disable multicast, since we don't support it, and broadcast
1980 unless we need it */
1981 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001982
1983 ret = daemon->dhcp_opts;
1984
1985 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
1986 return ret;
1987
1988 for (i = 0; i < NUM_OPTS; i++)
1989 {
1990 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
1991 fake_opts[i].netid = NULL;
1992 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
1993 }
1994
1995 /* create the data for the PXE_MENU and PXE_SERVERS options. */
1996 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001997 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001998
1999 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2000 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2001 {
2002 size_t len = strlen(service->menu);
2003 /* opt 43 max size is 255. encapsulated option has type and length
2004 bytes, so its max size is 253. */
2005 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2006 {
2007 *(p++) = service->type >> 8;
2008 *(p++) = service->type;
2009 *(p++) = len;
2010 memcpy(p, service->menu, len);
2011 p += len;
2012 i++;
2013 }
2014 else
2015 {
2016 toobig:
2017 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2018 return daemon->dhcp_opts;
2019 }
2020
Simon Kelley751d6f42012-02-10 15:24:51 +00002021 boot_server = service->basename ? local :
2022 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2023
Simon Kelley316e2732010-01-22 20:16:09 +00002024 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002025 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002026 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002027 goto toobig;
2028
2029 /* Boot service with known address - give it */
2030 *(q++) = service->type >> 8;
2031 *(q++) = service->type;
2032 *(q++) = 1;
2033 /* dest misaligned */
2034 memcpy(q, &boot_server.s_addr, INADDRSZ);
2035 q += INADDRSZ;
2036 }
2037 else if (service->type != 0)
2038 /* We don't know the server for a service type, so we'll
2039 allow the client to broadcast for it */
2040 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002041 }
2042
2043 /* if no prompt, wait forever if there's a choice */
2044 fake_prompt[0] = (i > 1) ? 255 : 0;
2045
2046 if (i == 0)
2047 discovery_control = 8; /* no menu - just use use mess->filename */
2048 else
2049 {
2050 ret = &fake_opts[j--];
2051 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2052 ret->val = (unsigned char *)daemon->dhcp_buff;
2053 ret->opt = SUBOPT_PXE_MENU;
2054
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002055 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002056 {
2057 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002058 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2059 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002060 ret->opt = SUBOPT_PXE_SERVERS;
2061 }
2062 }
2063
2064 for (o = daemon->dhcp_opts; o; o = o->next)
2065 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2066 break;
2067
2068 if (!o)
2069 {
2070 ret = &fake_opts[j--];
2071 ret->len = sizeof(fake_prompt);
2072 ret->val = fake_prompt;
2073 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2074 }
2075
Simon Kelley316e2732010-01-22 20:16:09 +00002076 ret = &fake_opts[j--];
2077 ret->len = 1;
2078 ret->opt = SUBOPT_PXE_DISCOVERY;
2079 ret->val= &discovery_control;
2080
Simon Kelley7622fc02009-06-04 20:32:05 +01002081 return ret;
2082}
2083
2084static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2085{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002086 memset(mess->sname, 0, sizeof(mess->sname));
2087 memset(mess->file, 0, sizeof(mess->file));
2088 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2089 mess->siaddr.s_addr = 0;
2090}
Simon Kelleycdeda282006-03-16 20:16:06 +00002091
Simon Kelley7622fc02009-06-04 20:32:05 +01002092struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002093{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002094 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002095
2096 /* decide which dhcp-boot option we're using */
2097 for (boot = daemon->boot_config; boot; boot = boot->next)
2098 if (match_netid(boot->netid, netid, 0))
2099 break;
2100 if (!boot)
2101 /* No match, look for one without a netid */
2102 for (boot = daemon->boot_config; boot; boot = boot->next)
2103 if (match_netid(boot->netid, netid, 1))
2104 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002105
2106 return boot;
2107}
2108
2109static void do_options(struct dhcp_context *context,
2110 struct dhcp_packet *mess,
2111 unsigned char *end,
2112 unsigned char *req_options,
2113 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002114 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002115 struct dhcp_netid *netid,
2116 struct in_addr subnet_addr,
2117 unsigned char fqdn_flags,
2118 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002119 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002120 int vendor_class_len,
2121 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002122{
2123 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2124 struct dhcp_boot *boot;
2125 unsigned char *p;
2126 int i, len, force_encap = 0;
2127 unsigned char f0 = 0, s0 = 0;
2128 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002129 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002130 struct dhcp_netid *tagif;
2131 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002132
Simon Kelley4cb1b322012-02-06 14:30:41 +00002133 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002134 if (context)
2135 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002136 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002137
Simon Kelley7622fc02009-06-04 20:32:05 +01002138 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002139 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002140 {
2141 char *q = daemon->namebuff;
2142 for (i = 0; req_options[i] != OPTION_END; i++)
2143 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002144 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002145 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2146 "%d%s%s%s",
2147 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002148 strlen(s) != 0 ? ":" : "",
2149 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002150 req_options[i+1] == OPTION_END ? "" : ", ");
2151 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2152 {
2153 q = daemon->namebuff;
2154 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2155 }
2156 }
2157 }
2158
Simon Kelley7de060b2011-08-26 17:24:52 +01002159 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2160 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2161 break;
2162 if (id_list)
2163 mess->flags |= htons(0x8000); /* force broadcast */
2164
Simon Kelley73a08a22009-02-05 20:28:08 +00002165 if (context)
2166 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002167
2168 /* See if we can send the boot stuff as options.
2169 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002170 and very old DHCP clients won't have this, we also
2171 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002172 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002173 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002174 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002175 {
2176 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002177 {
Simon Kelley28866e92011-02-14 20:19:14 +00002178 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002179 req_options &&
2180 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002181 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2182 else
Simon Kelley824af852008-02-12 20:43:05 +00002183 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002184 }
2185
2186 if (boot->file)
2187 {
Simon Kelley28866e92011-02-14 20:19:14 +00002188 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002189 req_options &&
2190 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002191 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2192 else
Simon Kelley824af852008-02-12 20:43:05 +00002193 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002194 }
2195
Simon Kelley7de060b2011-08-26 17:24:52 +01002196 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002197 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002198 else if (boot->tftp_sname)
2199 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002200 }
Simon Kelley824af852008-02-12 20:43:05 +00002201 else
2202 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002203 they're not explicitly asked for as options. OPTION_END is used
2204 as an internal way to specify siaddr without using dhcp-boot, for use in
2205 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002206 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002207 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002208 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002209 {
2210 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2211 done_file = 1;
2212 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002213
Simon Kelley824af852008-02-12 20:43:05 +00002214 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002215 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002216 {
2217 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2218 done_server = 1;
2219 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002220
Simon Kelley7de060b2011-08-26 17:24:52 +01002221 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002222 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002223 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002224
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002225 /* We don't want to do option-overload for BOOTP, so make the file and sname
2226 fields look like they are in use, even when they aren't. This gets restored
2227 at the end of this function. */
2228
Simon Kelley28866e92011-02-14 20:19:14 +00002229 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002230 {
2231 f0 = mess->file[0];
2232 mess->file[0] = 1;
2233 s0 = mess->sname[0];
2234 mess->sname[0] = 1;
2235 }
2236
2237 /* At this point, if mess->sname or mess->file are zeroed, they are available
2238 for option overload, reserve space for the overload option. */
2239 if (mess->file[0] == 0 || mess->sname[0] == 0)
2240 end -= 3;
2241
Simon Kelley3be34542004-09-11 19:12:13 +01002242 /* rfc3011 says this doesn't need to be in the requested options list. */
2243 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002244 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002245
2246 /* replies to DHCPINFORM may not have a valid context */
2247 if (context)
2248 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002249 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002250 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2251
2252 /* May not have a "guessed" broadcast address if we got no packets via a relay
2253 from this net yet (ie just unicast renewals after a restart */
2254 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002255 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002256 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2257
2258 /* Same comments as broadcast apply, and also may not be able to get a sensible
2259 default when using subnet select. User must configure by steam in that case. */
2260 if (context->router.s_addr &&
2261 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002262 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002263 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2264
Simon Kelleya21e27b2013-02-17 16:41:35 +00002265 if (daemon->port == NAMESERVER_PORT &&
2266 in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002267 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002268 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2269 }
Simon Kelley3be34542004-09-11 19:12:13 +01002270
Simon Kelley9009d742008-11-14 20:04:27 +00002271 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002272 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002273 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002274
Simon Kelley824af852008-02-12 20:43:05 +00002275 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002276 if (hostname)
2277 {
Simon Kelley824af852008-02-12 20:43:05 +00002278 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002279 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002280 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002281
2282 if (fqdn_flags != 0)
2283 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002284 len = strlen(hostname) + 3;
2285
Simon Kelley3d8df262005-08-29 12:19:27 +01002286 if (fqdn_flags & 0x04)
2287 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002288 else if (null_term)
2289 len++;
2290
Simon Kelley9009d742008-11-14 20:04:27 +00002291 if (domain)
2292 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002293
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002294 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002295 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002296 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002297 *(p++) = 255;
2298 *(p++) = 255;
2299
2300 if (fqdn_flags & 0x04)
2301 {
2302 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002303 if (domain)
2304 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002305 *p++ = 0;
2306 }
2307 else
2308 {
2309 memcpy(p, hostname, strlen(hostname));
2310 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002311 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002312 {
2313 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002314 memcpy(p, domain, strlen(domain));
2315 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002316 }
2317 if (null_term)
2318 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002319 }
2320 }
2321 }
2322 }
2323
Simon Kelley6b010842007-02-12 20:32:07 +00002324 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002325 {
Simon Kelley824af852008-02-12 20:43:05 +00002326 int optno = opt->opt;
2327
Simon Kelley7de060b2011-08-26 17:24:52 +01002328 /* netids match and not encapsulated? */
2329 if (!(opt->flags & DHOPT_TAGOK))
2330 continue;
2331
Simon Kelley6b010842007-02-12 20:32:07 +00002332 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002333 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002334 continue;
2335
2336 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002337 if (optno == OPTION_CLIENT_FQDN ||
2338 optno == OPTION_MAXMESSAGE ||
2339 optno == OPTION_OVERLOAD ||
2340 optno == OPTION_PAD ||
2341 optno == OPTION_END)
2342 continue;
2343
2344 if (optno == OPTION_SNAME && done_server)
2345 continue;
2346
2347 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002348 continue;
2349
Simon Kelley33820b72004-04-03 21:10:00 +01002350 /* For the options we have default values on
2351 dhc-option=<optionno> means "don't include this option"
2352 not "include a zero-length option" */
2353 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002354 (optno == OPTION_NETMASK ||
2355 optno == OPTION_BROADCAST ||
2356 optno == OPTION_ROUTER ||
2357 optno == OPTION_DNSSERVER ||
2358 optno == OPTION_DOMAINNAME ||
2359 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002360 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002361
2362 /* vendor-class comes from elsewhere for PXE */
2363 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2364 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002365
Simon Kelley7622fc02009-06-04 20:32:05 +01002366 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002367 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002368 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002369
Simon Kelley824af852008-02-12 20:43:05 +00002370 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002371 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002372 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002373 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2374
Simon Kelley6b010842007-02-12 20:32:07 +00002375 /* If we send a vendor-id, revisit which vendor-ops we consider
2376 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002377 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002378 {
2379 match_vendor_opts(p - 2, config_opts);
2380 done_vendor_class = 1;
2381 }
Simon Kelley6b010842007-02-12 20:32:07 +00002382 }
2383 }
2384
Simon Kelley73a08a22009-02-05 20:28:08 +00002385 /* Now send options to be encapsulated in arbitrary options,
2386 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002387 Also handle vendor-identifying vendor-encapsulated options,
2388 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002389 The may be more that one "outer" to do, so group
2390 all the options which match each outer in turn. */
2391 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002392 opt->flags &= ~DHOPT_ENCAP_DONE;
2393
2394 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002395 {
2396 int flags;
2397
2398 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2399 {
2400 int found = 0;
2401 struct dhcp_opt *o;
2402
2403 if (opt->flags & DHOPT_ENCAP_DONE)
2404 continue;
2405
2406 for (len = 0, o = config_opts; o; o = o->next)
2407 {
2408 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2409
2410 o->flags &= ~DHOPT_ENCAP_MATCH;
2411
2412 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2413 continue;
2414
2415 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002416 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002417 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2418 {
2419 o->flags |= DHOPT_ENCAP_MATCH;
2420 found = 1;
2421 len += do_opt(o, NULL, NULL, 0) + 2;
2422 }
2423 }
2424
2425 if (found)
2426 {
2427 if (flags & DHOPT_ENCAPSULATE)
2428 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2429 else if (len > 250)
2430 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2431 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2432 {
2433 int swap_ent = htonl(opt->u.encap);
2434 memcpy(p, &swap_ent, 4);
2435 p += 4;
2436 *(p++) = len;
2437 for (o = config_opts; o; o = o->next)
2438 if (o->flags & DHOPT_ENCAP_MATCH)
2439 {
2440 len = do_opt(o, p + 2, NULL, 0);
2441 *(p++) = o->opt;
2442 *(p++) = len;
2443 p += len;
2444 }
2445 }
2446 }
2447 }
2448 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002449
Simon Kelley7de060b2011-08-26 17:24:52 +01002450 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002451
Simon Kelley316e2732010-01-22 20:16:09 +00002452 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002453 {
2454 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002455 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002456 }
2457
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002458 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2459 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2460 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2461 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2462 /* If we send vendor encapsulated options, and haven't already sent option 60,
2463 echo back the value we got from the client. */
2464 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2465
Simon Kelley7622fc02009-06-04 20:32:05 +01002466 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002467 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002468 {
2469 mess->file[0] = f0;
2470 mess->sname[0] = s0;
2471 }
2472}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002473
Simon Kelley7622fc02009-06-04 20:32:05 +01002474#endif
2475
2476
2477
2478
2479
2480
2481