blob: 3ec4bc1c16e02d3d72815d8b25b3d5ea87c42a77 [file] [log] [blame]
Simon Kelleycdeda282006-03-16 20:16:06 +00001/* dnsmasq is Copyright (c) 2000-2006 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
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
Simon Kelley9e4abcb2004-01-22 19:47:41 +000013#include "dnsmasq.h"
14
15#define BOOTREQUEST 1
16#define BOOTREPLY 2
17#define DHCP_COOKIE 0x63825363
18
Simon Kelleya2226412004-05-13 20:27:08 +010019/* The Linux in-kernel DHCP client silently ignores any packet
20 smaller than this. Sigh........... */
21#define MIN_PACKETSZ 300
22
Simon Kelley9e4abcb2004-01-22 19:47:41 +000023#define OPTION_PAD 0
24#define OPTION_NETMASK 1
25#define OPTION_ROUTER 3
26#define OPTION_DNSSERVER 6
27#define OPTION_HOSTNAME 12
28#define OPTION_DOMAINNAME 15
29#define OPTION_BROADCAST 28
Simon Kelley91dccd02005-03-31 17:48:32 +010030#define OPTION_VENDOR_CLASS_OPT 43
Simon Kelley9e4abcb2004-01-22 19:47:41 +000031#define OPTION_REQUESTED_IP 50
32#define OPTION_LEASE_TIME 51
33#define OPTION_OVERLOAD 52
34#define OPTION_MESSAGE_TYPE 53
35#define OPTION_SERVER_IDENTIFIER 54
36#define OPTION_REQUESTED_OPTIONS 55
Simon Kelley44a2a312004-03-10 20:04:35 +000037#define OPTION_MESSAGE 56
Simon Kelley9e4abcb2004-01-22 19:47:41 +000038#define OPTION_MAXMESSAGE 57
Simon Kelley44a2a312004-03-10 20:04:35 +000039#define OPTION_T1 58
40#define OPTION_T2 59
Simon Kelleya84fa1d2004-04-23 22:21:21 +010041#define OPTION_VENDOR_ID 60
Simon Kelley44a2a312004-03-10 20:04:35 +000042#define OPTION_CLIENT_ID 61
Simon Kelleya2226412004-05-13 20:27:08 +010043#define OPTION_USER_CLASS 77
Simon Kelley3d8df262005-08-29 12:19:27 +010044#define OPTION_CLIENT_FQDN 81
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010045#define OPTION_AGENT_ID 82
Simon Kelley3be34542004-09-11 19:12:13 +010046#define OPTION_SUBNET_SELECT 118
Simon Kelley9e4abcb2004-01-22 19:47:41 +000047#define OPTION_END 255
48
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010049#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
50
Simon Kelley9e4abcb2004-01-22 19:47:41 +000051#define DHCPDISCOVER 1
52#define DHCPOFFER 2
53#define DHCPREQUEST 3
54#define DHCPDECLINE 4
55#define DHCPACK 5
56#define DHCPNAK 6
57#define DHCPRELEASE 7
58#define DHCPINFORM 8
59
Simon Kelley0a852542005-03-23 20:28:59 +000060#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010061#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
62#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
Simon Kelley0a852542005-03-23 20:28:59 +000063
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010064static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
65 struct dhcp_lease *lease, unsigned char *opt, time_t now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000066static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010067static unsigned char *option_end(unsigned char *p, unsigned char *end,
68 unsigned char *agent_id, struct dhcp_packet *start);
Simon Kelleycdeda282006-03-16 20:16:06 +000069static unsigned char *option_put_string(unsigned char *p, unsigned char *end,
70 int opt, char *string, int null_term);
Simon Kelley26128d22004-11-14 16:43:54 +000071static void bootp_option_put(struct dhcp_packet *mess,
72 struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000073static struct in_addr option_addr(unsigned char *opt);
Simon Kelley44a2a312004-03-10 20:04:35 +000074static unsigned int option_uint(unsigned char *opt, int size);
Simon Kelley16972692006-10-16 20:04:18 +010075static void log_packet(struct daemon *daemon, char *type, void *addr,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010076 struct dhcp_packet *mess, char *interface, char *string);
Simon Kelleycdeda282006-03-16 20:16:06 +000077static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010078static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000079static unsigned char *do_req_options(struct dhcp_context *context,
80 unsigned char *p, unsigned char *end,
81 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +010082 struct daemon *daemon,
83 char *hostname,
Simon Kelley3be34542004-09-11 19:12:13 +010084 struct dhcp_netid *netid,
Simon Kelley3d8df262005-08-29 12:19:27 +010085 struct in_addr subnet_addr,
Simon Kelleycdeda282006-03-16 20:16:06 +000086 unsigned char fqdn_flags,
87 int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000088
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010089
Simon Kelleycdeda282006-03-16 20:16:06 +000090size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name,
91 size_t sz, time_t now, int unicast_dest)
Simon Kelley33820b72004-04-03 21:10:00 +010092{
Simon Kelley26128d22004-11-14 16:43:54 +000093 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000094 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010095 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000096 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000097 struct dhcp_netid_list *id_list;
98 int clid_len = 0, ignore = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010099 struct dhcp_packet *mess = daemon->dhcp_packet.iov_base;
100 unsigned char *p, *end = (unsigned char *)(mess + 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100101 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000102 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100103 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +0000104 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000105 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000106 struct dhcp_config *config;
Simon Kelleya2226412004-05-13 20:27:08 +0100107 struct dhcp_netid *netid = NULL;
Simon Kelleyaedef832006-01-22 14:02:31 +0000108 struct in_addr subnet_addr, fallback;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100109 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100110 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100111 unsigned char fqdn_flags = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100112 unsigned char *agent_id = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +0100113
Simon Kelley849a8352006-06-09 21:02:31 +0100114 subnet_addr.s_addr = 0;
115
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100116 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100117 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000118
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100119 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000120 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100121
Simon Kelley3be34542004-09-11 19:12:13 +0100122 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000123 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000124 {
Simon Kelley3be34542004-09-11 19:12:13 +0100125 mess_type = option_uint(opt, 1);
Simon Kelley44a2a312004-03-10 20:04:35 +0000126
Simon Kelley3be34542004-09-11 19:12:13 +0100127 /* only insist on a cookie for DHCP. */
128 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
129 return 0;
130
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100131 /* two things to note here: expand_buf may move the packet,
132 so reassign mess from daemon->packet. Also, the size
133 sent includes the IP and UDP headers, hence the magic "-28" */
134 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
135 {
136 size_t size = (size_t)option_uint(opt, 2) - 28;
137
138 if (size > DHCP_PACKET_MAX)
139 size = DHCP_PACKET_MAX;
140 else if (size < sizeof(struct dhcp_packet))
141 size = sizeof(struct dhcp_packet);
142
143 if (expand_buf(&daemon->dhcp_packet, size))
144 {
145 mess = daemon->dhcp_packet.iov_base;
146 end = ((unsigned char *)mess) + size;
147 }
148 }
149
Simon Kelley3be34542004-09-11 19:12:13 +0100150 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
151 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000152 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100153 mess->ciaddr.s_addr = 0;
154
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100155 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
156 {
157 /* Any agent-id needs to be copied back out, verbatim, as the last option
158 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
159 get overwritten, then it will be shuffled back at the end of processing.
160 Note that the incoming options must not be overwritten here, so there has to
161 be enough free space at the end of the packet to copy the option. */
162 unsigned int total = option_len(opt) + 2;
163 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
164 if (last_opt && last_opt < end - total)
165 {
166 agent_id = end - total;
167 memcpy(agent_id, opt, total);
168 }
169
170 /* look for RFC3527 Link selection sub-option */
171 if ((opt = option_find1(option_ptr(opt), option_ptr(opt) + option_len(opt), SUBOPT_SUBNET_SELECT, INADDRSZ)))
172 subnet_addr = option_addr(opt);
173 }
174
175 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
176 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100177 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000178
179 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000180 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000181 {
Simon Kelley26128d22004-11-14 16:43:54 +0000182 clid_len = option_len(opt);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000183 clid = option_ptr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000184 }
Simon Kelley0a852542005-03-23 20:28:59 +0000185
186 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100187 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000188
189 /* If this request is missing a clid, but we've seen one before,
190 use it again for option matching etc. */
191 if (lease && !clid && lease->clid)
192 {
193 clid_len = lease->clid_len;
194 clid = lease->clid;
195 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000196 }
Simon Kelley3be34542004-09-11 19:12:13 +0100197
Simon Kelleycdeda282006-03-16 20:16:06 +0000198 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
199 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100200 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
201 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000202 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100203 mac->netid.next = netid;
204 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000205 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100206
Simon Kelley0a852542005-03-23 20:28:59 +0000207 /* Determine network for this packet. Our caller will have already linked all the
208 contexts which match the addresses of the receiving interface but if the
209 machine has an address already, or came via a relay, or we have a subnet selector,
210 we search again. If we don't have have a giaddr or explicit subnet selector,
211 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100212 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
213 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000214 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
215 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100216 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100217 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100218 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000219
Simon Kelley3d8df262005-08-29 12:19:27 +0100220 if (subnet_addr.s_addr)
221 {
222 addr = subnet_addr;
223 force = 1;
224 }
225 else if (mess->giaddr.s_addr)
226 {
227 addr = mess->giaddr;
228 force = 1;
229 }
Simon Kelley16972692006-10-16 20:04:18 +0100230 else
231 {
232 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
233 addr = mess->ciaddr;
234 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
235 if (context_tmp->netmask.s_addr &&
236 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
237 is_same_net(addr, context_tmp->end, context_tmp->netmask))
238 {
239 context_new = context;
240 break;
241 }
242 }
243
244 if (!context_new)
245 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
246 if (context_tmp->netmask.s_addr &&
247 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
248 is_same_net(addr, context_tmp->end, context_tmp->netmask))
249 {
250 context_tmp->current = context_new;
251 context_new = context_tmp;
252 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100253
Simon Kelley3d8df262005-08-29 12:19:27 +0100254 if (context_new || force)
255 context = context_new;
Simon Kelley16972692006-10-16 20:04:18 +0100256
Simon Kelley0a852542005-03-23 20:28:59 +0000257 }
Simon Kelley3be34542004-09-11 19:12:13 +0100258
259 if (!context)
260 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000261 syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"),
262 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
Simon Kelley0a852542005-03-23 20:28:59 +0000263 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 +0100264 return 0;
265 }
266
Simon Kelleyaedef832006-01-22 14:02:31 +0000267 /* keep _a_ local address available. */
268 fallback = context->local;
269
Simon Kelley3be34542004-09-11 19:12:13 +0100270 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100271 p = mess->options + sizeof(u32); /* skip cookie */
272
Simon Kelleycdeda282006-03-16 20:16:06 +0000273 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
274 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley26128d22004-11-14 16:43:54 +0000275
Simon Kelley3be34542004-09-11 19:12:13 +0100276 if (mess_type == 0)
277 {
278 /* BOOTP request */
Simon Kelley26128d22004-11-14 16:43:54 +0000279 struct dhcp_netid id;
280 char save = mess->file[128];
281 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100282
283 /* must have a MAC addr for bootp */
284 if (mess->htype == 0 || mess->hlen == 0)
285 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000286
Simon Kelley26128d22004-11-14 16:43:54 +0000287 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000288 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000289
290 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
291
292 if (have_config(config, CONFIG_NAME))
293 hostname = config->hostname;
294
295 if (have_config(config, CONFIG_NETID))
296 {
297 config->netid.next = netid;
298 netid = &config->netid;
299 }
300
301 /* Match incoming filename field as a netid. */
302 if (mess->file[0])
303 {
304 mess->file[128] = 0; /* ensure zero term. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100305 id.net = (char *)mess->file;
Simon Kelley26128d22004-11-14 16:43:54 +0000306 id.next = netid;
307 netid = &id;
308 }
309
310 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000311 if (match_netid(id_list->list, netid, 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000312 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000313
Simon Kelley3d8df262005-08-29 12:19:27 +0100314 if (!message)
315 {
316 if (have_config(config, CONFIG_ADDR))
317 {
318 logaddr = &config->addr;
319 mess->yiaddr = config->addr;
320 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000321 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100322 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000323 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000324 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100325 }
326 else if (!(daemon->options & OPT_BOOTP_DYNAMIC))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000327 message = _("no address configured");
Simon Kelley3d8df262005-08-29 12:19:27 +0100328 else
329 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000330 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelleye17fb622006-01-14 20:33:46 +0000331 !address_available(context, lease->addr))
332 {
333 if (lease)
334 {
335 /* lease exists, wrong network. */
336 lease_prune(lease, now);
337 lease = NULL;
338 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000339 if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000340 message = _("no address available");
341 }
342 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100343 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100344 }
345
Simon Kelley7cebd202006-05-06 14:13:33 +0100346 if (!message &&
347 !lease &&
348 (!(lease = lease_allocate(mess->yiaddr))))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000349 message = _("no leases left");
Simon Kelley3d8df262005-08-29 12:19:27 +0100350
Simon Kelleye17fb622006-01-14 20:33:46 +0000351 if (!message && !(context = narrow_context(context, mess->yiaddr)))
352 message = _("wrong network");
353
Simon Kelley3d8df262005-08-29 12:19:27 +0100354 if (!message)
355 {
356 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100357
Simon Kelleycdeda282006-03-16 20:16:06 +0000358 if (context->netid.net)
Simon Kelley3d8df262005-08-29 12:19:27 +0100359 {
360 context->netid.next = netid;
361 netid = &context->netid;
362 }
363
Simon Kelleycdeda282006-03-16 20:16:06 +0000364 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100365 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000366 lease_set_hostname(lease, hostname, daemon->domain_suffix, 1);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100367 lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
Simon Kelley3d8df262005-08-29 12:19:27 +0100368
369 p = do_req_options(context, p, end, NULL, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000370 hostname, netid, subnet_addr, fqdn_flags, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100371 /* must do this after do_req_options since it overwrites filename field. */
372 mess->siaddr = context->local;
373 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100374 p = option_end(p, end, NULL, mess);
Simon Kelley3d8df262005-08-29 12:19:27 +0100375 }
376 }
377
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100378 log_packet(daemon, NULL, logaddr, mess, iface_name, message);
Simon Kelley26128d22004-11-14 16:43:54 +0000379 mess->file[128] = save;
380
381 if (message)
382 return 0;
383 else
384 return p - (unsigned char *)mess;
Simon Kelley3be34542004-09-11 19:12:13 +0100385 }
386
Simon Kelley3d8df262005-08-29 12:19:27 +0100387 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
388 {
389 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
390 int len = option_len(opt);
391 char *pq = daemon->dhcp_buff;
392 unsigned char *pp, *op = option_ptr(opt);
393
394 fqdn_flags = *op;
395 len -= 3;
396 op += 3;
397 pp = op;
398
399 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000400 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100401 fqdn_flags |= 0x02;
402
403 fqdn_flags &= ~0x08;
404 fqdn_flags |= 0x01;
405
406 if (fqdn_flags & 0x04)
407 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
408 {
409 memcpy(pq, op+1, *op);
410 pq += *op;
411 op += (*op)+1;
412 *(pq++) = '.';
413 }
414 else
415 {
416 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000417 if (len > 0 && op[len-1] == 0)
418 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100419 pq += len + 1;
420 }
421
422 if (pq != daemon->dhcp_buff)
423 pq--;
424
425 *pq = 0;
426
427 if (canonicalise(daemon->dhcp_buff))
428 offer_hostname = client_hostname = daemon->dhcp_buff;
429 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000430 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000431 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000432 int len = option_len(opt);
Simon Kelley3d8df262005-08-29 12:19:27 +0100433 memcpy(daemon->dhcp_buff, option_ptr(opt), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000434 /* Microsoft clients are broken, and need zero-terminated strings
435 in options. We detect this state here, and do the same in
436 any options we send */
437 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
438 borken_opt = 1;
439 else
440 daemon->dhcp_buff[len] = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100441 if (canonicalise(daemon->dhcp_buff))
442 client_hostname = daemon->dhcp_buff;
443 }
444
445 if (have_config(config, CONFIG_NAME))
446 {
447 hostname = config->hostname;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000448 hostname_auth = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100449 /* be careful not to send an OFFER with a hostname not
450 matching the DISCOVER. */
451 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
452 offer_hostname = hostname;
453 }
454 else if (client_hostname && (hostname = strip_hostname(daemon, client_hostname)) && !config)
455 {
456 /* Search again now we have a hostname.
457 Only accept configs without CLID and HWADDR here, (they won't match)
458 to avoid impersonation by name. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000459 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
460 mess->chaddr, mess->hlen,
461 mess->htype, hostname);
Simon Kelley3d8df262005-08-29 12:19:27 +0100462 if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
463 config = new;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000464 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100465
Simon Kelleya2226412004-05-13 20:27:08 +0100466 if (have_config(config, CONFIG_NETID))
467 {
468 config->netid.next = netid;
469 netid = &config->netid;
470 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100471
Simon Kelley26128d22004-11-14 16:43:54 +0000472 /* user-class options are, according to RFC3004, supposed to contain
473 a set of counted strings. Here we check that this is so (by seeing
474 if the counts are consistent with the overall option length) and if
475 so zero the counts so that we don't get spurious matches between
476 the vendor string and the counts. If the lengths don't add up, we
477 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100478 and just do the substring match. dhclient provides these broken options.
479 The code, later, which sends user-class data to the lease-change script
480 relies on the transformation done here.
481 */
Simon Kelleya2226412004-05-13 20:27:08 +0100482
Simon Kelleybb01cb92004-12-13 20:56:23 +0000483 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100484 {
Simon Kelley26128d22004-11-14 16:43:54 +0000485 unsigned char *ucp = option_ptr(opt);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000486 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000487 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
488 if (j == option_len(opt))
489 for (j = 0; j < option_len(opt); j = tmp)
490 {
491 tmp = j + ucp[j] + 1;
492 ucp[j] = 0;
493 }
Simon Kelleya2226412004-05-13 20:27:08 +0100494 }
Simon Kelley26128d22004-11-14 16:43:54 +0000495
496 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000497 if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000498 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000499 int i;
Simon Kelley26128d22004-11-14 16:43:54 +0000500 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
501 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
502 {
503 vendor->netid.next = netid;
504 netid = &vendor->netid;
505 break;
506 }
507 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000508
Simon Kelley26128d22004-11-14 16:43:54 +0000509 /* if all the netids in the ignore list are present, ignore this client */
510 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000511 if (match_netid(id_list->list, netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000512 ignore = 1;
513
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100514 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
515 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000516 clid = NULL;
517
Simon Kelleybb01cb92004-12-13 20:56:23 +0000518 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100519 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100520 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000521 memcpy(req_options, option_ptr(opt), option_len(opt));
522 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100523 }
524
Simon Kelley3be34542004-09-11 19:12:13 +0100525 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000526 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000527 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000528 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000529 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000530 return 0;
531
532 /* sanitise any message. Paranoid? Moi? */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000533 if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000534 {
Simon Kelley3be34542004-09-11 19:12:13 +0100535 char *p = option_ptr(opt), *q = daemon->dhcp_buff;
Simon Kelley44a2a312004-03-10 20:04:35 +0000536 int i;
537
538 for (i = option_len(opt); i > 0; i--)
539 {
540 char c = *p++;
541 if (isprint(c))
542 *q++ = c;
543 }
544 *q++ = 0; /* add terminator */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000545 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000546
Simon Kelleybb01cb92004-12-13 20:56:23 +0000547 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000548 return 0;
549
Simon Kelley849a8352006-06-09 21:02:31 +0100550 log_packet(daemon, "DECLINE", option_ptr(opt), mess, iface_name, daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000551
552 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
553 lease_prune(lease, now);
554
Simon Kelley33820b72004-04-03 21:10:00 +0100555 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000556 config->addr.s_addr == option_addr(opt).s_addr)
557 {
Simon Kelley849a8352006-06-09 21:02:31 +0100558 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
559 syslog(LOG_WARNING, _("disabling DHCP static address %s for %s"),
560 inet_ntoa(config->addr), daemon->dhcp_buff);
561 config->flags |= CONFIG_DECLINED;
562 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000563 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100564 else
565 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100566 for (; context; context = context->current)
567 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000568
569 return 0;
570
571 case DHCPRELEASE:
Simon Kelley16972692006-10-16 20:04:18 +0100572 if (!(context = narrow_context(context, mess->ciaddr)) ||
573 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000574 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000575 return 0;
576
Simon Kelley44a2a312004-03-10 20:04:35 +0000577 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
578 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100579 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000580 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100581
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100582 log_packet(daemon, "RELEASE", &mess->ciaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000583
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000584 return 0;
585
586 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100587 if (ignore || have_config(config, CONFIG_DISABLE))
588 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000589 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100590 opt = NULL;
591 }
592 else
593 {
594 struct in_addr addr, conf;
595
596 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
597 addr = option_addr(opt);
598
599 conf.s_addr = 0;
600 if (have_config(config, CONFIG_ADDR))
601 {
Simon Kelley849a8352006-06-09 21:02:31 +0100602 char *addrs = inet_ntoa(config->addr);
603
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100604 if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease)
605 syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley849a8352006-06-09 21:02:31 +0100606 addrs, print_mac(daemon, ltmp->hwaddr, ltmp->hwaddr_len));
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100607 else
608 {
609 struct dhcp_context *tmp;
610 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100611 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100612 break;
613 if (tmp)
Simon Kelley849a8352006-06-09 21:02:31 +0100614 syslog(LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
615 else if (have_config(config, CONFIG_DECLINED) &&
616 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
617 syslog(LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100618 else
619 conf = config->addr;
620 }
621 }
622
623 if (conf.s_addr)
624 mess->yiaddr = conf;
625 else if (lease && address_available(context, lease->addr))
626 mess->yiaddr = lease->addr;
627 else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
628 !config_find_by_address(daemon->dhcp_conf, addr))
629 mess->yiaddr = addr;
630 else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
631 message = _("no address available");
632 }
633
Simon Kelley16972692006-10-16 20:04:18 +0100634 log_packet(daemon, "DISCOVER", opt ? option_ptr(opt) : NULL, mess, iface_name, message);
Simon Kelley3d8df262005-08-29 12:19:27 +0100635
Simon Kelleye17fb622006-01-14 20:33:46 +0000636 if (message || !(context = narrow_context(context, mess->yiaddr)))
Simon Kelley33820b72004-04-03 21:10:00 +0100637 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +0000638
Simon Kelleycdeda282006-03-16 20:16:06 +0000639 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000640 {
641 context->netid.next = netid;
642 netid = &context->netid;
643 }
644
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100645 time = calc_time(context, config, lease, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelley0a852542005-03-23 20:28:59 +0000646 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000647 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000648 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley0a852542005-03-23 20:28:59 +0000649 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000650 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100651 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +0000652 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100653 {
Simon Kelley59353a62004-11-21 19:34:28 +0000654 p = option_put(p, end, OPTION_T1, 4, (time/2));
655 p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100656 }
Simon Kelley3be34542004-09-11 19:12:13 +0100657 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000658 offer_hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100659 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000660
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100661 log_packet(daemon, "OFFER" , &mess->yiaddr, mess, iface_name, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000662 return p - (unsigned char *)mess;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000663
664 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +0000665 if (ignore || have_config(config, CONFIG_DISABLE))
666 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000667 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000668 {
669 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000670 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000671
Simon Kelleybb01cb92004-12-13 20:56:23 +0000672 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000673 {
Simon Kelley3be34542004-09-11 19:12:13 +0100674 /* SELECTING */
Simon Kelleye17fb622006-01-14 20:33:46 +0000675 for (; context; context = context->current)
676 if (context->local.s_addr == option_addr(opt).s_addr)
677 break;
678
679 if (!context)
Simon Kelley3be34542004-09-11 19:12:13 +0100680 return 0;
681
682 /* If a lease exists for this host and another address, squash it. */
683 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
684 {
685 lease_prune(lease, now);
686 lease = NULL;
687 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000688 }
Simon Kelley3be34542004-09-11 19:12:13 +0100689 else
690 {
691 /* INIT-REBOOT */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100692 if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +0100693 return 0;
694
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100695 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000696 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +0100697 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000698 }
699 else
700 {
701 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +0000702 /* Check existing lease for this address.
703 We allow it to be missing if dhcp-authoritative mode
704 as long as we can allocate the lease now - checked below.
705 This makes for a smooth recovery from a lost lease DB */
706 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
707 (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000708 {
709 message = _("lease not found");
710 /* ensure we broadcast NAK */
711 unicast_dest = 0;
712 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100713 /* desynchronise renewals */
714 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +0100715 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000716 }
717
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100718 log_packet(daemon, "REQUEST", &mess->yiaddr, mess, iface_name, NULL);
Simon Kelleye17fb622006-01-14 20:33:46 +0000719
Simon Kelleydfa666f2004-08-02 18:27:27 +0100720 if (!message)
721 {
722 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100723 struct dhcp_context *tmp = NULL;
724
725 if (have_config(config, CONFIG_ADDR))
726 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100727 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100728 break;
Simon Kelleyaedef832006-01-22 14:02:31 +0000729
Simon Kelleye17fb622006-01-14 20:33:46 +0000730 if (!(context = narrow_context(context, mess->yiaddr)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000731 {
Simon Kelleye17fb622006-01-14 20:33:46 +0000732 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000733 message = _("wrong network");
734 /* ensure we broadcast NAK */
735 unicast_dest = 0;
736 }
737
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100738 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelleydfa666f2004-08-02 18:27:27 +0100739 else if (!address_available(context, mess->yiaddr) &&
740 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000741 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +0000742
Simon Kelleydfa666f2004-08-02 18:27:27 +0100743 /* Check if a new static address has been configured. Be very sure that
744 when the client does DISCOVER, it will get the static address, otherwise
745 an endless protocol loop will ensue. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100746 else if (!tmp &&
747 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +0100748 (!have_config(config, CONFIG_DECLINED) ||
749 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100750 config->addr.s_addr != mess->yiaddr.s_addr &&
751 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000752 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100753
754 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100755 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000756 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100757
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100758 else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000759 message = _("address in use");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100760
Simon Kelley7cebd202006-05-06 14:13:33 +0100761 else if (!clid && mess->hlen == 0)
762 message = _("no unique-id");
763
Simon Kelley16972692006-10-16 20:04:18 +0100764 else if (!lease)
765 {
766 if (!(lease = lease_allocate(mess->yiaddr)))
767 message = _("no leases left");
768 else
769 {
770 /* copy user-class and vendor class into new lease, for the script */
771 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
772 {
773 int len = option_len(opt);
774 unsigned char *ucp = option_ptr(opt);
775 /* If the user-class option started as counted strings, the first byte will be zero. */
776 if (len != 0 && ucp[0] == 0)
777 ucp++, len--;
778 if ((lease->userclass = malloc(len+1)))
779 {
780 memcpy(lease->userclass, ucp, len);
781 lease->userclass[len] = 0;
782 lease->userclass_len = len+1;
783 }
784 }
785 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
786 {
787 int len = option_len(opt);
788 unsigned char *ucp = option_ptr(opt);
789 if ((lease->vendorclass = malloc(len+1)))
790 {
791 memcpy(lease->vendorclass, ucp, len);
792 lease->vendorclass[len] = 0;
793 lease->vendorclass_len = len+1;
794 }
795 }
796 }
797 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100798 }
Simon Kelley16972692006-10-16 20:04:18 +0100799
Simon Kelley44a2a312004-03-10 20:04:35 +0000800 if (message)
801 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100802 log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000803
Simon Kelleyb8187c82005-11-26 21:46:27 +0000804 mess->siaddr.s_addr = mess->yiaddr.s_addr = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000805 bootp_option_put(mess, NULL, NULL);
806 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelleyaedef832006-01-22 14:02:31 +0000807 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
808 ntohl(context ? context->local.s_addr : fallback.s_addr));
Simon Kelleycdeda282006-03-16 20:16:06 +0000809 p = option_put_string(p, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000810 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
811 a distant subnet which unicast a REQ to us won't work. */
812 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
813 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
814 {
815 mess->flags |= htons(0x8000); /* broadcast */
816 mess->ciaddr.s_addr = 0;
817 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000818 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100819 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000820 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000821 if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr)))
822 {
823 hostname = client_hostname;
824 hostname_auth = 1;
825 }
826
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100827 log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100828
Simon Kelleycdeda282006-03-16 20:16:06 +0000829 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000830 {
831 context->netid.next = netid;
832 netid = &context->netid;
833 }
834
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100835 time = calc_time(context, config, NULL, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelleycdeda282006-03-16 20:16:06 +0000836 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100837 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000838 lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100839 lease_set_expires(lease, time, now);
Simon Kelley0a852542005-03-23 20:28:59 +0000840
841 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000842 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100843 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000844 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000845 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
846 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100847 {
Simon Kelley59353a62004-11-21 19:34:28 +0000848 while (fuzz > (time/16))
849 fuzz = fuzz/2;
850 p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100851 p = option_put(p, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100852 }
853 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000854 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000855 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100856
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100857 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000858 return p - (unsigned char *)mess;
859
860 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +0000861 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000862 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +0100863
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100864 log_packet(daemon, "INFORM", &mess->ciaddr, mess, iface_name, message);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100865
Simon Kelleye17fb622006-01-14 20:33:46 +0000866 if (message || mess->ciaddr.s_addr == 0 ||
867 !(context = narrow_context(context, mess->ciaddr)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100868 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000869
Simon Kelley59353a62004-11-21 19:34:28 +0000870 if (context->netid.net)
871 {
872 context->netid.next = netid;
873 netid = &context->netid;
874 }
875
Simon Kelley0a852542005-03-23 20:28:59 +0000876 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000877 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000878 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000879 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000880 if (!hostname)
881 hostname = host_from_dns(daemon, mess->yiaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100882 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000883 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100884 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000885
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100886 log_packet(daemon, "ACK", &mess->ciaddr, mess, iface_name, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000887 return p - (unsigned char *)mess;
888 }
889
890 return 0;
891}
892
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100893static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
894 struct dhcp_lease *lease, unsigned char *opt, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000895{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100896 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +0000897
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100898 if (opt)
899 {
900 unsigned int req_time = option_uint(opt, 4);
901 if (req_time < 120 )
902 req_time = 120; /* sanity */
903 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
904 time = req_time;
905 }
906 else if (lease && lease->expires != 0 && difftime(lease->expires, now) > 0.0)
907 {
908 unsigned int lease_time = (unsigned int)difftime(lease->expires, now);
909
910 /* put a floor on lease-remaining time. */
911 if (lease_time < 360 )
912 lease_time = 360;
913
914 if (time > lease_time)
915 time = lease_time;
916 }
917
918 return time;
919}
920
Simon Kelley16972692006-10-16 20:04:18 +0100921static void log_packet(struct daemon *daemon, char *type, void *addr,
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100922 struct dhcp_packet *mess, char *interface, char *string)
923{
Simon Kelley16972692006-10-16 20:04:18 +0100924 struct in_addr a;
925
926 /* addr may be misaligned */
927 if (addr)
928 memcpy(&a, addr, sizeof(a));
929
Simon Kelley7cebd202006-05-06 14:13:33 +0100930 syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
Simon Kelley3be34542004-09-11 19:12:13 +0100931 type ? "DHCP" : "BOOTP",
932 type ? type : "",
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000933 interface,
Simon Kelley16972692006-10-16 20:04:18 +0100934 addr ? inet_ntoa(a) : "",
Simon Kelleycdeda282006-03-16 20:16:06 +0000935 addr ? " " : "",
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100936 print_mac(daemon, mess->chaddr, mess->hlen),
Simon Kelley44a2a312004-03-10 20:04:35 +0000937 string ? string : "");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000938}
939
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000940static struct in_addr option_addr(unsigned char *opt)
941{
942 /* this worries about unaligned data in the option. */
943 /* struct in_addr is network byte order */
944 struct in_addr ret;
945
946 memcpy(&ret, option_ptr(opt), INADDRSZ);
947
948 return ret;
949}
950
Simon Kelley44a2a312004-03-10 20:04:35 +0000951static unsigned int option_uint(unsigned char *opt, int size)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000952{
953 /* this worries about unaligned data and byte order */
Simon Kelley44a2a312004-03-10 20:04:35 +0000954 unsigned int ret = 0;
955 int i;
956 unsigned char *p = option_ptr(opt);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000957
Simon Kelley44a2a312004-03-10 20:04:35 +0000958 for (i = 0; i < size; i++)
959 ret = (ret << 8) | *p++;
960
961 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000962}
963
Simon Kelley26128d22004-11-14 16:43:54 +0000964static void bootp_option_put(struct dhcp_packet *mess,
965 struct dhcp_boot *boot_opts, struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000966{
Simon Kelley26128d22004-11-14 16:43:54 +0000967 struct dhcp_boot *tmp;
968
969 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000970 if (match_netid(tmp->netid, netids, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000971 break;
972 if (!tmp)
973 /* No match, look for one without a netid */
974 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000975 if (match_netid(tmp->netid, netids, 1))
Simon Kelley26128d22004-11-14 16:43:54 +0000976 break;
977
978 /* Do this _after_ the matching above, since in
979 BOOTP mode, one if the things we match is the filename. */
980
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000981 memset(mess->sname, 0, sizeof(mess->sname));
982 memset(mess->file, 0, sizeof(mess->file));
Simon Kelley26128d22004-11-14 16:43:54 +0000983
984 if (tmp)
985 {
986 if (tmp->sname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100987 strncpy((char *)mess->sname, tmp->sname, sizeof(mess->sname)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000988 if (tmp->file)
Simon Kelley3d8df262005-08-29 12:19:27 +0100989 strncpy((char *)mess->file, tmp->file, sizeof(mess->file)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000990 if (tmp->next_server.s_addr)
991 mess->siaddr = tmp->next_server;
992 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000993}
994
Simon Kelleyb8187c82005-11-26 21:46:27 +0000995static int check_space(unsigned char *p, unsigned char *end, int len, int opt)
996{
997 /* always keep one octet space for the END option. */
998 if (p + len + 3 >= end)
999 {
1000 syslog(LOG_WARNING, _("cannot send DHCP option %d: no space left in packet"), opt);
1001 return 0;
1002 }
1003
1004 return 1;
1005}
1006
1007
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001008static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
1009{
1010 int i;
Simon Kelley44a2a312004-03-10 20:04:35 +00001011
Simon Kelleyb8187c82005-11-26 21:46:27 +00001012 if (check_space(p, end, len, opt))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001013 {
1014 *(p++) = opt;
Simon Kelleya2226412004-05-13 20:27:08 +01001015 *(p++) = len;
1016
1017 for (i = 0; i < len; i++)
1018 *(p++) = val >> (8 * (len - (i + 1)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001019 }
1020 return p;
1021}
1022
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001023static unsigned char *option_end(unsigned char *p, unsigned char *end,
1024 unsigned char *agent_id, struct dhcp_packet *start)
Simon Kelleya2226412004-05-13 20:27:08 +01001025{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001026 /* shuffle agent-id back down if we have room for it */
1027 if (agent_id && p < agent_id)
1028 {
1029 memmove(p, agent_id, end - agent_id);
1030 p += end - agent_id;
1031 }
1032
Simon Kelleya2226412004-05-13 20:27:08 +01001033 *(p++) = OPTION_END;
1034 while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
1035 *p++ = 0;
1036
1037 return p;
1038}
1039
Simon Kelleycdeda282006-03-16 20:16:06 +00001040static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt,
1041 char *string, int null_term)
Simon Kelley44a2a312004-03-10 20:04:35 +00001042{
Simon Kelley0a852542005-03-23 20:28:59 +00001043 size_t len = strlen(string);
Simon Kelley3be34542004-09-11 19:12:13 +01001044
Simon Kelleycdeda282006-03-16 20:16:06 +00001045 if (null_term && len != 255)
1046 len++;
1047
Simon Kelleyb8187c82005-11-26 21:46:27 +00001048 if (check_space(p, end, len, opt))
Simon Kelley44a2a312004-03-10 20:04:35 +00001049 {
1050 *(p++) = opt;
Simon Kelley3be34542004-09-11 19:12:13 +01001051 *(p++) = len;
1052 memcpy(p, string, len);
1053 p += len;
Simon Kelley44a2a312004-03-10 20:04:35 +00001054 }
Simon Kelley3be34542004-09-11 19:12:13 +01001055
Simon Kelley44a2a312004-03-10 20:04:35 +00001056 return p;
1057}
1058
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001059static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001060{
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001061 while (*p != OPTION_END)
1062 {
Simon Kelleybb01cb92004-12-13 20:56:23 +00001063 if (p >= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001064 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001065 else if (*p == OPTION_PAD)
1066 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001067 else
1068 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001069 int opt_len;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001070 if (p >= end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001071 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001072 opt_len = option_len(p);
Simon Kelleybb01cb92004-12-13 20:56:23 +00001073 if (p >= end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001074 return NULL; /* malformed packet */
1075 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001076 return p;
1077 p += opt_len + 2;
1078 }
1079 }
1080
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001081 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001082}
1083
Simon Kelleycdeda282006-03-16 20:16:06 +00001084static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001085{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001086 unsigned char *ret, *opt;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001087
Simon Kelley3be34542004-09-11 19:12:13 +01001088 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001089 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1090 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001091
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001092 /* look for overload option. */
1093 if (!(opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
1094 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001095
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001096 /* Can we look in filename area ? */
1097 if ((option_uint(opt, 1) & 1) &&
1098 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1099 return ret;
1100
1101 /* finally try sname area */
1102 if ((option_uint(opt, 1) & 2) &&
1103 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1104 return ret;
1105
1106 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001107}
1108
1109static int in_list(unsigned char *list, int opt)
1110{
1111 int i;
1112
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001113 /* If no requested options, send everything, not nothing. */
1114 if (!list)
1115 return 1;
1116
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001117 for (i = 0; list[i] != OPTION_END; i++)
1118 if (opt == list[i])
1119 return 1;
1120
1121 return 0;
1122}
1123
Simon Kelleya2226412004-05-13 20:27:08 +01001124static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001125{
Simon Kelley91dccd02005-03-31 17:48:32 +01001126 struct dhcp_opt *tmp;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001127 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelleya2226412004-05-13 20:27:08 +01001128 if (tmp->opt == opt)
Simon Kelleycdeda282006-03-16 20:16:06 +00001129 if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
1130 return tmp;
Simon Kelleya2226412004-05-13 20:27:08 +01001131
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001132 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001133}
1134
Simon Kelleycdeda282006-03-16 20:16:06 +00001135static unsigned char *do_opt(struct dhcp_opt *opt, unsigned char *p, unsigned char *end,
1136 struct in_addr local, int null_term)
Simon Kelley91dccd02005-03-31 17:48:32 +01001137{
Simon Kelleycdeda282006-03-16 20:16:06 +00001138 int len = opt->len;
1139
1140 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1141 len++;
1142
1143 if (!check_space(p, end, len, opt->opt))
Simon Kelley91dccd02005-03-31 17:48:32 +01001144 return p;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001145
Simon Kelley91dccd02005-03-31 17:48:32 +01001146 *(p++) = opt->opt;
Simon Kelleycdeda282006-03-16 20:16:06 +00001147 *(p++) = len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001148
Simon Kelleycdeda282006-03-16 20:16:06 +00001149 if (len == 0)
Simon Kelley91dccd02005-03-31 17:48:32 +01001150 return p;
1151
Simon Kelleycdeda282006-03-16 20:16:06 +00001152 if (opt->flags & DHOPT_ADDR)
Simon Kelley91dccd02005-03-31 17:48:32 +01001153 {
1154 int j;
1155 struct in_addr *a = (struct in_addr *)opt->val;
1156 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1157 {
1158 /* zero means "self" (but not in vendorclass options.) */
1159 if (a->s_addr == 0)
1160 memcpy(p, &local, INADDRSZ);
1161 else
1162 memcpy(p, a, INADDRSZ);
1163 p += INADDRSZ;
1164 }
1165 }
1166 else
1167 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001168 memcpy(p, opt->val, len);
1169 p += len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001170 }
1171
1172 return p;
1173}
1174
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001175static unsigned char *do_req_options(struct dhcp_context *context,
1176 unsigned char *p, unsigned char *end,
1177 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +01001178 struct daemon *daemon,
1179 char *hostname,
Simon Kelley3be34542004-09-11 19:12:13 +01001180 struct dhcp_netid *netid,
Simon Kelley3d8df262005-08-29 12:19:27 +01001181 struct in_addr subnet_addr,
Simon Kelleycdeda282006-03-16 20:16:06 +00001182 unsigned char fqdn_flags,
1183 int null_term)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001184{
Simon Kelley3be34542004-09-11 19:12:13 +01001185 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
Simon Kelley91dccd02005-03-31 17:48:32 +01001186 char *vendor_class = NULL;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001187
Simon Kelley3be34542004-09-11 19:12:13 +01001188 /* rfc3011 says this doesn't need to be in the requested options list. */
1189 if (subnet_addr.s_addr)
1190 p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
1191
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001192 if (in_list(req_options, OPTION_NETMASK) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001193 !option_find2(netid, config_opts, OPTION_NETMASK))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001194 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
1195
Simon Kelley3be34542004-09-11 19:12:13 +01001196 /* May not have a "guessed" broadcast address if we got no packets via a relay
1197 from this net yet (ie just unicast renewals after a restart */
1198 if (context->broadcast.s_addr &&
1199 in_list(req_options, OPTION_BROADCAST) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001200 !option_find2(netid, config_opts, OPTION_BROADCAST))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001201 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
1202
Simon Kelley3be34542004-09-11 19:12:13 +01001203 /* Same comments as broadcast apply, and also may not be able to get a sensible
1204 default when using subnet select. User must configure by steam in that case. */
1205 if (context->router.s_addr &&
1206 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001207 !option_find2(netid, config_opts, OPTION_ROUTER))
Simon Kelley3be34542004-09-11 19:12:13 +01001208 p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001209
1210 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001211 !option_find2(netid, config_opts, OPTION_DNSSERVER))
Simon Kelley0a852542005-03-23 20:28:59 +00001212 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001213
Simon Kelley3be34542004-09-11 19:12:13 +01001214 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001215 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001216 p = option_put_string(p, end, OPTION_DOMAINNAME, daemon->domain_suffix, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001217
1218 /* Note that we ignore attempts to set the hostname using
Simon Kelley3d8df262005-08-29 12:19:27 +01001219 --dhcp-option=12,<name> and the fqdn using
1220 --dhc-option=81,<name> */
1221 if (hostname)
1222 {
1223 if (in_list(req_options, OPTION_HOSTNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001224 p = option_put_string(p, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01001225
1226 if (fqdn_flags != 0)
1227 {
1228 int len = strlen(hostname) + 3;
1229 if (fqdn_flags & 0x04)
1230 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00001231 else if (null_term)
1232 len++;
1233
Simon Kelley3d8df262005-08-29 12:19:27 +01001234 if (daemon->domain_suffix)
1235 len += strlen(daemon->domain_suffix) + 1;
1236
1237 if (p + len + 1 < end)
1238 {
1239 *(p++) = OPTION_CLIENT_FQDN;
1240 *(p++) = len;
1241 *(p++) = fqdn_flags;
1242 *(p++) = 255;
1243 *(p++) = 255;
1244
1245 if (fqdn_flags & 0x04)
1246 {
1247 p = do_rfc1035_name(p, hostname);
1248 if (daemon->domain_suffix)
1249 p = do_rfc1035_name(p, daemon->domain_suffix);
1250 *p++ = 0;
1251 }
1252 else
1253 {
1254 memcpy(p, hostname, strlen(hostname));
1255 p += strlen(hostname);
1256 if (daemon->domain_suffix)
1257 {
1258 *(p++) = '.';
1259 memcpy(p, daemon->domain_suffix, strlen(daemon->domain_suffix));
1260 p += strlen(daemon->domain_suffix);
Simon Kelleycdeda282006-03-16 20:16:06 +00001261 }
1262 if (null_term)
1263 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01001264 }
1265 }
1266 }
1267 }
1268
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001269 for (opt=config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001270 {
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001271 if (opt->opt == OPTION_HOSTNAME ||
Simon Kelley3d8df262005-08-29 12:19:27 +01001272 opt->opt == OPTION_CLIENT_FQDN ||
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001273 opt->opt == OPTION_MAXMESSAGE ||
1274 !in_list(req_options, opt->opt) ||
Simon Kelley91dccd02005-03-31 17:48:32 +01001275 opt != option_find2(netid, config_opts, opt->opt))
Simon Kelley33820b72004-04-03 21:10:00 +01001276 continue;
1277
1278 /* For the options we have default values on
1279 dhc-option=<optionno> means "don't include this option"
1280 not "include a zero-length option" */
1281 if (opt->len == 0 &&
1282 (opt->opt == OPTION_NETMASK ||
1283 opt->opt == OPTION_BROADCAST ||
1284 opt->opt == OPTION_ROUTER ||
1285 opt->opt == OPTION_DNSSERVER))
1286 continue;
Simon Kelley91dccd02005-03-31 17:48:32 +01001287
1288 /* opt->val has terminating zero */
1289 if (opt->opt == OPTION_VENDOR_ID)
Simon Kelley3d8df262005-08-29 12:19:27 +01001290 vendor_class = (char *)opt->val;
Simon Kelley91dccd02005-03-31 17:48:32 +01001291 else
Simon Kelleycdeda282006-03-16 20:16:06 +00001292 p = do_opt(opt, p, end, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001293 }
1294
1295 if (in_list(req_options, OPTION_VENDOR_ID))
1296 {
1297 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001298 if (match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0))
Simon Kelley91dccd02005-03-31 17:48:32 +01001299 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001300 if (vendor_class && strcmp(vendor_class, (char *)opt->vendor_class) != 0)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001301 syslog(LOG_WARNING, _("More than one vendor class matches, using %s"), vendor_class);
Simon Kelley91dccd02005-03-31 17:48:32 +01001302 else
Simon Kelley3d8df262005-08-29 12:19:27 +01001303 vendor_class = (char *)opt->vendor_class;
Simon Kelley91dccd02005-03-31 17:48:32 +01001304 }
1305
1306 if (vendor_class)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001307 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001308 p = option_put_string(p, end, OPTION_VENDOR_ID, vendor_class, 0);
Simon Kelley91dccd02005-03-31 17:48:32 +01001309
1310 if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
Simon Kelley1ab84e22004-01-29 16:48:35 +00001311 {
Simon Kelley91dccd02005-03-31 17:48:32 +01001312 unsigned char *plen, *oend = end;
1313
1314 /* encapsulated options can only be 256 bytes,
1315 even of the packet is larger */
1316 if (p + 256 < end)
1317 oend = p + 256;
1318
1319 if (p + 3 >= oend)
1320 return p;
1321
1322 *(p++) = OPTION_VENDOR_CLASS_OPT;
1323 plen = p++; /* fill in later */
1324
1325 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001326 if ((match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0)) &&
Simon Kelley3d8df262005-08-29 12:19:27 +01001327 strcmp(vendor_class, (char *)opt->vendor_class) == 0)
Simon Kelleycdeda282006-03-16 20:16:06 +00001328 p = do_opt(opt, p, oend, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001329
1330 *plen = p - plen - 1;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001331 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001332 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001333 }
1334
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001335 return p;
1336}
1337
1338