blob: 80ae761542c409f60b4a428fb78e9b140f8d08b3 [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 Kelley5e9e0ef2006-04-17 14:24:29 +010075static char *print_mac(struct daemon *daemon, unsigned char *mac, int len);
76static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
77 struct dhcp_packet *mess, char *interface, char *string);
Simon Kelleycdeda282006-03-16 20:16:06 +000078static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010079static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080static unsigned char *do_req_options(struct dhcp_context *context,
81 unsigned char *p, unsigned char *end,
82 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +010083 struct daemon *daemon,
84 char *hostname,
Simon Kelley3be34542004-09-11 19:12:13 +010085 struct dhcp_netid *netid,
Simon Kelley3d8df262005-08-29 12:19:27 +010086 struct in_addr subnet_addr,
Simon Kelleycdeda282006-03-16 20:16:06 +000087 unsigned char fqdn_flags,
88 int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000089
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010090
Simon Kelleycdeda282006-03-16 20:16:06 +000091size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name,
92 size_t sz, time_t now, int unicast_dest)
Simon Kelley33820b72004-04-03 21:10:00 +010093{
Simon Kelley26128d22004-11-14 16:43:54 +000094 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000095 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010096 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000097 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000098 struct dhcp_netid_list *id_list;
99 int clid_len = 0, ignore = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100100 struct dhcp_packet *mess = daemon->dhcp_packet.iov_base;
101 unsigned char *p, *end = (unsigned char *)(mess + 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100102 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000103 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100104 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +0000105 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000106 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000107 struct dhcp_config *config;
Simon Kelleya2226412004-05-13 20:27:08 +0100108 struct dhcp_netid *netid = NULL;
Simon Kelleyaedef832006-01-22 14:02:31 +0000109 struct in_addr subnet_addr, fallback;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100110 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100111 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100112 unsigned char fqdn_flags = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100113 subnet_addr.s_addr = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100114 unsigned char *agent_id = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +0100115
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;
217 struct in_addr addr = mess->ciaddr;
218 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 }
230
231 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley0a852542005-03-23 20:28:59 +0000232 if (context_tmp->netmask.s_addr &&
233 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
234 is_same_net(addr, context_tmp->end, context_tmp->netmask))
235 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100236 context_tmp->current = context_new;
237 context_new = context_tmp;
Simon Kelley0a852542005-03-23 20:28:59 +0000238 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100239
240 if (context_new || force)
241 context = context_new;
242
Simon Kelley0a852542005-03-23 20:28:59 +0000243 }
Simon Kelley3be34542004-09-11 19:12:13 +0100244
245 if (!context)
246 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000247 syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"),
248 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
Simon Kelley0a852542005-03-23 20:28:59 +0000249 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 +0100250 return 0;
251 }
252
Simon Kelleyaedef832006-01-22 14:02:31 +0000253 /* keep _a_ local address available. */
254 fallback = context->local;
255
Simon Kelley3be34542004-09-11 19:12:13 +0100256 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100257 p = mess->options + sizeof(u32); /* skip cookie */
258
Simon Kelleycdeda282006-03-16 20:16:06 +0000259 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
260 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley26128d22004-11-14 16:43:54 +0000261
Simon Kelley3be34542004-09-11 19:12:13 +0100262 if (mess_type == 0)
263 {
264 /* BOOTP request */
Simon Kelley26128d22004-11-14 16:43:54 +0000265 struct dhcp_netid id;
266 char save = mess->file[128];
267 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100268
269 /* must have a MAC addr for bootp */
270 if (mess->htype == 0 || mess->hlen == 0)
271 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000272
Simon Kelley26128d22004-11-14 16:43:54 +0000273 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000274 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000275
276 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
277
278 if (have_config(config, CONFIG_NAME))
279 hostname = config->hostname;
280
281 if (have_config(config, CONFIG_NETID))
282 {
283 config->netid.next = netid;
284 netid = &config->netid;
285 }
286
287 /* Match incoming filename field as a netid. */
288 if (mess->file[0])
289 {
290 mess->file[128] = 0; /* ensure zero term. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100291 id.net = (char *)mess->file;
Simon Kelley26128d22004-11-14 16:43:54 +0000292 id.next = netid;
293 netid = &id;
294 }
295
296 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000297 if (match_netid(id_list->list, netid, 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000298 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000299
Simon Kelley3d8df262005-08-29 12:19:27 +0100300 if (!message)
301 {
302 if (have_config(config, CONFIG_ADDR))
303 {
304 logaddr = &config->addr;
305 mess->yiaddr = config->addr;
306 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000307 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100308 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000309 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000310 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100311 }
312 else if (!(daemon->options & OPT_BOOTP_DYNAMIC))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000313 message = _("no address configured");
Simon Kelley3d8df262005-08-29 12:19:27 +0100314 else
315 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000316 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelleye17fb622006-01-14 20:33:46 +0000317 !address_available(context, lease->addr))
318 {
319 if (lease)
320 {
321 /* lease exists, wrong network. */
322 lease_prune(lease, now);
323 lease = NULL;
324 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000325 if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000326 message = _("no address available");
327 }
328 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100329 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100330 }
331
Simon Kelleycdeda282006-03-16 20:16:06 +0000332 if (!message && !lease && (!(lease = lease_allocate(mess->chaddr, NULL, mess->hlen, mess->htype, 0, mess->yiaddr))))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000333 message = _("no leases left");
Simon Kelley3d8df262005-08-29 12:19:27 +0100334
Simon Kelleye17fb622006-01-14 20:33:46 +0000335 if (!message && !(context = narrow_context(context, mess->yiaddr)))
336 message = _("wrong network");
337
Simon Kelley3d8df262005-08-29 12:19:27 +0100338 if (!message)
339 {
340 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100341
Simon Kelleycdeda282006-03-16 20:16:06 +0000342 if (context->netid.net)
Simon Kelley3d8df262005-08-29 12:19:27 +0100343 {
344 context->netid.next = netid;
345 netid = &context->netid;
346 }
347
Simon Kelleycdeda282006-03-16 20:16:06 +0000348 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100349 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000350 lease_set_hostname(lease, hostname, daemon->domain_suffix, 1);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100351 lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
Simon Kelley3d8df262005-08-29 12:19:27 +0100352
353 p = do_req_options(context, p, end, NULL, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000354 hostname, netid, subnet_addr, fqdn_flags, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100355 /* must do this after do_req_options since it overwrites filename field. */
356 mess->siaddr = context->local;
357 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100358 p = option_end(p, end, NULL, mess);
Simon Kelley3d8df262005-08-29 12:19:27 +0100359 }
360 }
361
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100362 log_packet(daemon, NULL, logaddr, mess, iface_name, message);
Simon Kelley26128d22004-11-14 16:43:54 +0000363 mess->file[128] = save;
364
365 if (message)
366 return 0;
367 else
368 return p - (unsigned char *)mess;
Simon Kelley3be34542004-09-11 19:12:13 +0100369 }
370
Simon Kelley3d8df262005-08-29 12:19:27 +0100371 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
372 {
373 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
374 int len = option_len(opt);
375 char *pq = daemon->dhcp_buff;
376 unsigned char *pp, *op = option_ptr(opt);
377
378 fqdn_flags = *op;
379 len -= 3;
380 op += 3;
381 pp = op;
382
383 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000384 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100385 fqdn_flags |= 0x02;
386
387 fqdn_flags &= ~0x08;
388 fqdn_flags |= 0x01;
389
390 if (fqdn_flags & 0x04)
391 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
392 {
393 memcpy(pq, op+1, *op);
394 pq += *op;
395 op += (*op)+1;
396 *(pq++) = '.';
397 }
398 else
399 {
400 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000401 if (len > 0 && op[len-1] == 0)
402 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100403 pq += len + 1;
404 }
405
406 if (pq != daemon->dhcp_buff)
407 pq--;
408
409 *pq = 0;
410
411 if (canonicalise(daemon->dhcp_buff))
412 offer_hostname = client_hostname = daemon->dhcp_buff;
413 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000414 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000415 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000416 int len = option_len(opt);
Simon Kelley3d8df262005-08-29 12:19:27 +0100417 memcpy(daemon->dhcp_buff, option_ptr(opt), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000418 /* Microsoft clients are broken, and need zero-terminated strings
419 in options. We detect this state here, and do the same in
420 any options we send */
421 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
422 borken_opt = 1;
423 else
424 daemon->dhcp_buff[len] = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100425 if (canonicalise(daemon->dhcp_buff))
426 client_hostname = daemon->dhcp_buff;
427 }
428
429 if (have_config(config, CONFIG_NAME))
430 {
431 hostname = config->hostname;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000432 hostname_auth = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100433 /* be careful not to send an OFFER with a hostname not
434 matching the DISCOVER. */
435 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
436 offer_hostname = hostname;
437 }
438 else if (client_hostname && (hostname = strip_hostname(daemon, client_hostname)) && !config)
439 {
440 /* Search again now we have a hostname.
441 Only accept configs without CLID and HWADDR here, (they won't match)
442 to avoid impersonation by name. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000443 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
444 mess->chaddr, mess->hlen,
445 mess->htype, hostname);
Simon Kelley3d8df262005-08-29 12:19:27 +0100446 if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
447 config = new;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000448 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100449
Simon Kelleya2226412004-05-13 20:27:08 +0100450 if (have_config(config, CONFIG_NETID))
451 {
452 config->netid.next = netid;
453 netid = &config->netid;
454 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100455
Simon Kelley26128d22004-11-14 16:43:54 +0000456 /* user-class options are, according to RFC3004, supposed to contain
457 a set of counted strings. Here we check that this is so (by seeing
458 if the counts are consistent with the overall option length) and if
459 so zero the counts so that we don't get spurious matches between
460 the vendor string and the counts. If the lengths don't add up, we
461 assume that the option is a single string and non RFC3004 compliant
462 and just do the substring match. dhclient provides these broken options. */
Simon Kelleya2226412004-05-13 20:27:08 +0100463
Simon Kelleybb01cb92004-12-13 20:56:23 +0000464 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100465 {
Simon Kelley26128d22004-11-14 16:43:54 +0000466 unsigned char *ucp = option_ptr(opt);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000467 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000468 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
469 if (j == option_len(opt))
470 for (j = 0; j < option_len(opt); j = tmp)
471 {
472 tmp = j + ucp[j] + 1;
473 ucp[j] = 0;
474 }
Simon Kelleya2226412004-05-13 20:27:08 +0100475 }
Simon Kelley26128d22004-11-14 16:43:54 +0000476
477 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000478 if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000479 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000480 int i;
Simon Kelley26128d22004-11-14 16:43:54 +0000481 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
482 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
483 {
484 vendor->netid.next = netid;
485 netid = &vendor->netid;
486 break;
487 }
488 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000489
Simon Kelley26128d22004-11-14 16:43:54 +0000490 /* if all the netids in the ignore list are present, ignore this client */
491 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000492 if (match_netid(id_list->list, netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000493 ignore = 1;
494
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100495 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
496 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000497 clid = NULL;
498
Simon Kelleybb01cb92004-12-13 20:56:23 +0000499 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100500 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100501 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000502 memcpy(req_options, option_ptr(opt), option_len(opt));
503 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100504 }
505
Simon Kelley3be34542004-09-11 19:12:13 +0100506 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000507 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000508 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000509 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000510 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000511 return 0;
512
513 /* sanitise any message. Paranoid? Moi? */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000514 if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000515 {
Simon Kelley3be34542004-09-11 19:12:13 +0100516 char *p = option_ptr(opt), *q = daemon->dhcp_buff;
Simon Kelley44a2a312004-03-10 20:04:35 +0000517 int i;
518
519 for (i = option_len(opt); i > 0; i--)
520 {
521 char c = *p++;
522 if (isprint(c))
523 *q++ = c;
524 }
525 *q++ = 0; /* add terminator */
Simon Kelley3be34542004-09-11 19:12:13 +0100526 message = daemon->dhcp_buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000527 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000528
Simon Kelleybb01cb92004-12-13 20:56:23 +0000529 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000530 return 0;
531
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100532 log_packet(daemon, "DECLINE", option_ptr(opt), mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000533
534 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
535 lease_prune(lease, now);
536
Simon Kelley33820b72004-04-03 21:10:00 +0100537 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000538 config->addr.s_addr == option_addr(opt).s_addr)
539 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000540 syslog(LOG_WARNING, _("disabling DHCP static address %s"), inet_ntoa(config->addr));
Simon Kelley33820b72004-04-03 21:10:00 +0100541 config->flags &= ~CONFIG_ADDR ;
Simon Kelley44a2a312004-03-10 20:04:35 +0000542 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100543 else
544 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100545 for (; context; context = context->current)
546 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000547
548 return 0;
549
550 case DHCPRELEASE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000551 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000552 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000553 return 0;
554
Simon Kelley44a2a312004-03-10 20:04:35 +0000555 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
556 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100557 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000558 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100559
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100560 log_packet(daemon, "RELEASE", &mess->ciaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000561
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000562 return 0;
563
564 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100565 if (ignore || have_config(config, CONFIG_DISABLE))
566 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000567 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100568 opt = NULL;
569 }
570 else
571 {
572 struct in_addr addr, conf;
573
574 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
575 addr = option_addr(opt);
576
577 conf.s_addr = 0;
578 if (have_config(config, CONFIG_ADDR))
579 {
580 if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease)
581 syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
582 inet_ntoa(config->addr), print_mac(daemon, ltmp->hwaddr, ltmp->hwaddr_len));
583 else
584 {
585 struct dhcp_context *tmp;
586 for (tmp = context; tmp; tmp = tmp->current)
587 if (context->local.s_addr == config->addr.s_addr)
588 break;
589 if (tmp)
590 syslog(LOG_WARNING, _("not using configured address %s because it is in use by the server"),
591 inet_ntoa(config->addr));
592 else
593 conf = config->addr;
594 }
595 }
596
597 if (conf.s_addr)
598 mess->yiaddr = conf;
599 else if (lease && address_available(context, lease->addr))
600 mess->yiaddr = lease->addr;
601 else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
602 !config_find_by_address(daemon->dhcp_conf, addr))
603 mess->yiaddr = addr;
604 else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
605 message = _("no address available");
606 }
607
608 log_packet(daemon, "DISCOVER", opt ? (struct in_addr *)option_ptr(opt) : NULL, mess, iface_name, message);
Simon Kelley3d8df262005-08-29 12:19:27 +0100609
Simon Kelleye17fb622006-01-14 20:33:46 +0000610 if (message || !(context = narrow_context(context, mess->yiaddr)))
Simon Kelley33820b72004-04-03 21:10:00 +0100611 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +0000612
Simon Kelleycdeda282006-03-16 20:16:06 +0000613 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000614 {
615 context->netid.next = netid;
616 netid = &context->netid;
617 }
618
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100619 time = calc_time(context, config, lease, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelley0a852542005-03-23 20:28:59 +0000620 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000621 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000622 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley0a852542005-03-23 20:28:59 +0000623 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000624 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100625 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +0000626 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100627 {
Simon Kelley59353a62004-11-21 19:34:28 +0000628 p = option_put(p, end, OPTION_T1, 4, (time/2));
629 p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100630 }
Simon Kelley3be34542004-09-11 19:12:13 +0100631 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000632 offer_hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100633 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000634
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100635 log_packet(daemon, "OFFER" , &mess->yiaddr, mess, iface_name, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000636 return p - (unsigned char *)mess;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000637
638 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +0000639 if (ignore || have_config(config, CONFIG_DISABLE))
640 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000641 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000642 {
643 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000645
Simon Kelleybb01cb92004-12-13 20:56:23 +0000646 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000647 {
Simon Kelley3be34542004-09-11 19:12:13 +0100648 /* SELECTING */
Simon Kelleye17fb622006-01-14 20:33:46 +0000649 for (; context; context = context->current)
650 if (context->local.s_addr == option_addr(opt).s_addr)
651 break;
652
653 if (!context)
Simon Kelley3be34542004-09-11 19:12:13 +0100654 return 0;
655
656 /* If a lease exists for this host and another address, squash it. */
657 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
658 {
659 lease_prune(lease, now);
660 lease = NULL;
661 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000662 }
Simon Kelley3be34542004-09-11 19:12:13 +0100663 else
664 {
665 /* INIT-REBOOT */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100666 if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +0100667 return 0;
668
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100669 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000670 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +0100671 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000672 }
673 else
674 {
675 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +0000676 /* Check existing lease for this address.
677 We allow it to be missing if dhcp-authoritative mode
678 as long as we can allocate the lease now - checked below.
679 This makes for a smooth recovery from a lost lease DB */
680 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
681 (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000682 {
683 message = _("lease not found");
684 /* ensure we broadcast NAK */
685 unicast_dest = 0;
686 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100687 /* desynchronise renewals */
688 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +0100689 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000690 }
691
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100692 log_packet(daemon, "REQUEST", &mess->yiaddr, mess, iface_name, NULL);
Simon Kelleye17fb622006-01-14 20:33:46 +0000693
Simon Kelleydfa666f2004-08-02 18:27:27 +0100694 if (!message)
695 {
696 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100697 struct dhcp_context *tmp = NULL;
698
699 if (have_config(config, CONFIG_ADDR))
700 for (tmp = context; tmp; tmp = tmp->current)
701 if (context->local.s_addr == config->addr.s_addr)
702 break;
Simon Kelleyaedef832006-01-22 14:02:31 +0000703
Simon Kelleye17fb622006-01-14 20:33:46 +0000704 if (!(context = narrow_context(context, mess->yiaddr)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000705 {
Simon Kelleye17fb622006-01-14 20:33:46 +0000706 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000707 message = _("wrong network");
708 /* ensure we broadcast NAK */
709 unicast_dest = 0;
710 }
711
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100712 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelleydfa666f2004-08-02 18:27:27 +0100713 else if (!address_available(context, mess->yiaddr) &&
714 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000715 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +0000716
Simon Kelleydfa666f2004-08-02 18:27:27 +0100717 /* Check if a new static address has been configured. Be very sure that
718 when the client does DISCOVER, it will get the static address, otherwise
719 an endless protocol loop will ensue. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100720 else if (!tmp &&
721 have_config(config, CONFIG_ADDR) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100722 config->addr.s_addr != mess->yiaddr.s_addr &&
723 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000724 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100725
726 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100727 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000728 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100729
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100730 else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000731 message = _("address in use");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100732
Simon Kelleycdeda282006-03-16 20:16:06 +0000733 else if (!lease && !(lease = lease_allocate(mess->chaddr, clid, mess->hlen, mess->htype, clid_len, mess->yiaddr)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000734 message = _("no leases left");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100735 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000736
737 if (message)
738 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100739 log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000740
Simon Kelleyb8187c82005-11-26 21:46:27 +0000741 mess->siaddr.s_addr = mess->yiaddr.s_addr = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000742 bootp_option_put(mess, NULL, NULL);
743 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelleyaedef832006-01-22 14:02:31 +0000744 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
745 ntohl(context ? context->local.s_addr : fallback.s_addr));
Simon Kelleycdeda282006-03-16 20:16:06 +0000746 p = option_put_string(p, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000747 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
748 a distant subnet which unicast a REQ to us won't work. */
749 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
750 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
751 {
752 mess->flags |= htons(0x8000); /* broadcast */
753 mess->ciaddr.s_addr = 0;
754 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000755 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100756 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000757 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000758 if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr)))
759 {
760 hostname = client_hostname;
761 hostname_auth = 1;
762 }
763
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100764 log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100765
Simon Kelleycdeda282006-03-16 20:16:06 +0000766 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000767 {
768 context->netid.next = netid;
769 netid = &context->netid;
770 }
771
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100772 time = calc_time(context, config, NULL, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelleycdeda282006-03-16 20:16:06 +0000773 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100774 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000775 lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100776 lease_set_expires(lease, time, now);
Simon Kelley0a852542005-03-23 20:28:59 +0000777
778 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000779 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100780 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000781 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000782 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
783 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100784 {
Simon Kelley59353a62004-11-21 19:34:28 +0000785 while (fuzz > (time/16))
786 fuzz = fuzz/2;
787 p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100788 p = option_put(p, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100789 }
790 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000791 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000792 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100793
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100794 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000795 return p - (unsigned char *)mess;
796
797 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +0000798 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000799 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +0100800
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100801 log_packet(daemon, "INFORM", &mess->ciaddr, mess, iface_name, message);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100802
Simon Kelleye17fb622006-01-14 20:33:46 +0000803 if (message || mess->ciaddr.s_addr == 0 ||
804 !(context = narrow_context(context, mess->ciaddr)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100805 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000806
Simon Kelley59353a62004-11-21 19:34:28 +0000807 if (context->netid.net)
808 {
809 context->netid.next = netid;
810 netid = &context->netid;
811 }
812
Simon Kelley0a852542005-03-23 20:28:59 +0000813 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000814 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000815 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000816 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000817 if (!hostname)
818 hostname = host_from_dns(daemon, mess->yiaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100819 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000820 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100821 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000822
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100823 log_packet(daemon, "ACK", &mess->ciaddr, mess, iface_name, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000824 return p - (unsigned char *)mess;
825 }
826
827 return 0;
828}
829
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100830static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
831 struct dhcp_lease *lease, unsigned char *opt, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000832{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100833 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +0000834
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100835 if (opt)
836 {
837 unsigned int req_time = option_uint(opt, 4);
838 if (req_time < 120 )
839 req_time = 120; /* sanity */
840 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
841 time = req_time;
842 }
843 else if (lease && lease->expires != 0 && difftime(lease->expires, now) > 0.0)
844 {
845 unsigned int lease_time = (unsigned int)difftime(lease->expires, now);
846
847 /* put a floor on lease-remaining time. */
848 if (lease_time < 360 )
849 lease_time = 360;
850
851 if (time > lease_time)
852 time = lease_time;
853 }
854
855 return time;
856}
857
858static char *print_mac(struct daemon *daemon, unsigned char *mac, int len)
859{
860 char *p = daemon->namebuff;
861 int i;
862
863 if (len == 0)
864 sprintf(p, "<null> ");
865 else
866 for (i = 0; i < len; i++)
867 p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? " " : ":");
868
869 return daemon->namebuff;
870}
871
872
873static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
874 struct dhcp_packet *mess, char *interface, char *string)
875{
Simon Kelleycdeda282006-03-16 20:16:06 +0000876 syslog(LOG_INFO, "%s%s(%s) %s%s%s%s",
Simon Kelley3be34542004-09-11 19:12:13 +0100877 type ? "DHCP" : "BOOTP",
878 type ? type : "",
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000879 interface,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000880 addr ? inet_ntoa(*addr) : "",
Simon Kelleycdeda282006-03-16 20:16:06 +0000881 addr ? " " : "",
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100882 print_mac(daemon, mess->chaddr, mess->hlen),
Simon Kelley44a2a312004-03-10 20:04:35 +0000883 string ? string : "");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000884}
885
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000886static struct in_addr option_addr(unsigned char *opt)
887{
888 /* this worries about unaligned data in the option. */
889 /* struct in_addr is network byte order */
890 struct in_addr ret;
891
892 memcpy(&ret, option_ptr(opt), INADDRSZ);
893
894 return ret;
895}
896
Simon Kelley44a2a312004-03-10 20:04:35 +0000897static unsigned int option_uint(unsigned char *opt, int size)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000898{
899 /* this worries about unaligned data and byte order */
Simon Kelley44a2a312004-03-10 20:04:35 +0000900 unsigned int ret = 0;
901 int i;
902 unsigned char *p = option_ptr(opt);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000903
Simon Kelley44a2a312004-03-10 20:04:35 +0000904 for (i = 0; i < size; i++)
905 ret = (ret << 8) | *p++;
906
907 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000908}
909
Simon Kelley26128d22004-11-14 16:43:54 +0000910static void bootp_option_put(struct dhcp_packet *mess,
911 struct dhcp_boot *boot_opts, struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000912{
Simon Kelley26128d22004-11-14 16:43:54 +0000913 struct dhcp_boot *tmp;
914
915 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000916 if (match_netid(tmp->netid, netids, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000917 break;
918 if (!tmp)
919 /* No match, look for one without a netid */
920 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000921 if (match_netid(tmp->netid, netids, 1))
Simon Kelley26128d22004-11-14 16:43:54 +0000922 break;
923
924 /* Do this _after_ the matching above, since in
925 BOOTP mode, one if the things we match is the filename. */
926
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000927 memset(mess->sname, 0, sizeof(mess->sname));
928 memset(mess->file, 0, sizeof(mess->file));
Simon Kelley26128d22004-11-14 16:43:54 +0000929
930 if (tmp)
931 {
932 if (tmp->sname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100933 strncpy((char *)mess->sname, tmp->sname, sizeof(mess->sname)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000934 if (tmp->file)
Simon Kelley3d8df262005-08-29 12:19:27 +0100935 strncpy((char *)mess->file, tmp->file, sizeof(mess->file)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000936 if (tmp->next_server.s_addr)
937 mess->siaddr = tmp->next_server;
938 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000939}
940
Simon Kelleyb8187c82005-11-26 21:46:27 +0000941static int check_space(unsigned char *p, unsigned char *end, int len, int opt)
942{
943 /* always keep one octet space for the END option. */
944 if (p + len + 3 >= end)
945 {
946 syslog(LOG_WARNING, _("cannot send DHCP option %d: no space left in packet"), opt);
947 return 0;
948 }
949
950 return 1;
951}
952
953
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000954static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
955{
956 int i;
Simon Kelley44a2a312004-03-10 20:04:35 +0000957
Simon Kelleyb8187c82005-11-26 21:46:27 +0000958 if (check_space(p, end, len, opt))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000959 {
960 *(p++) = opt;
Simon Kelleya2226412004-05-13 20:27:08 +0100961 *(p++) = len;
962
963 for (i = 0; i < len; i++)
964 *(p++) = val >> (8 * (len - (i + 1)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000965 }
966 return p;
967}
968
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100969static unsigned char *option_end(unsigned char *p, unsigned char *end,
970 unsigned char *agent_id, struct dhcp_packet *start)
Simon Kelleya2226412004-05-13 20:27:08 +0100971{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100972 /* shuffle agent-id back down if we have room for it */
973 if (agent_id && p < agent_id)
974 {
975 memmove(p, agent_id, end - agent_id);
976 p += end - agent_id;
977 }
978
Simon Kelleya2226412004-05-13 20:27:08 +0100979 *(p++) = OPTION_END;
980 while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
981 *p++ = 0;
982
983 return p;
984}
985
Simon Kelleycdeda282006-03-16 20:16:06 +0000986static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt,
987 char *string, int null_term)
Simon Kelley44a2a312004-03-10 20:04:35 +0000988{
Simon Kelley0a852542005-03-23 20:28:59 +0000989 size_t len = strlen(string);
Simon Kelley3be34542004-09-11 19:12:13 +0100990
Simon Kelleycdeda282006-03-16 20:16:06 +0000991 if (null_term && len != 255)
992 len++;
993
Simon Kelleyb8187c82005-11-26 21:46:27 +0000994 if (check_space(p, end, len, opt))
Simon Kelley44a2a312004-03-10 20:04:35 +0000995 {
996 *(p++) = opt;
Simon Kelley3be34542004-09-11 19:12:13 +0100997 *(p++) = len;
998 memcpy(p, string, len);
999 p += len;
Simon Kelley44a2a312004-03-10 20:04:35 +00001000 }
Simon Kelley3be34542004-09-11 19:12:13 +01001001
Simon Kelley44a2a312004-03-10 20:04:35 +00001002 return p;
1003}
1004
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001005static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001006{
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001007 while (*p != OPTION_END)
1008 {
Simon Kelleybb01cb92004-12-13 20:56:23 +00001009 if (p >= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001010 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001011 else if (*p == OPTION_PAD)
1012 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001013 else
1014 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001015 int opt_len;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001016 if (p >= end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001017 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001018 opt_len = option_len(p);
Simon Kelleybb01cb92004-12-13 20:56:23 +00001019 if (p >= end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001020 return NULL; /* malformed packet */
1021 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001022 return p;
1023 p += opt_len + 2;
1024 }
1025 }
1026
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001027 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001028}
1029
Simon Kelleycdeda282006-03-16 20:16:06 +00001030static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001031{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001032 unsigned char *ret, *opt;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001033
Simon Kelley3be34542004-09-11 19:12:13 +01001034 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001035 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1036 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001037
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001038 /* look for overload option. */
1039 if (!(opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
1040 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001041
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001042 /* Can we look in filename area ? */
1043 if ((option_uint(opt, 1) & 1) &&
1044 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1045 return ret;
1046
1047 /* finally try sname area */
1048 if ((option_uint(opt, 1) & 2) &&
1049 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1050 return ret;
1051
1052 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001053}
1054
1055static int in_list(unsigned char *list, int opt)
1056{
1057 int i;
1058
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001059 /* If no requested options, send everything, not nothing. */
1060 if (!list)
1061 return 1;
1062
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001063 for (i = 0; list[i] != OPTION_END; i++)
1064 if (opt == list[i])
1065 return 1;
1066
1067 return 0;
1068}
1069
Simon Kelleya2226412004-05-13 20:27:08 +01001070static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001071{
Simon Kelley91dccd02005-03-31 17:48:32 +01001072 struct dhcp_opt *tmp;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001073 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelleya2226412004-05-13 20:27:08 +01001074 if (tmp->opt == opt)
Simon Kelleycdeda282006-03-16 20:16:06 +00001075 if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
1076 return tmp;
Simon Kelleya2226412004-05-13 20:27:08 +01001077
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001078 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001079}
1080
Simon Kelleycdeda282006-03-16 20:16:06 +00001081static unsigned char *do_opt(struct dhcp_opt *opt, unsigned char *p, unsigned char *end,
1082 struct in_addr local, int null_term)
Simon Kelley91dccd02005-03-31 17:48:32 +01001083{
Simon Kelleycdeda282006-03-16 20:16:06 +00001084 int len = opt->len;
1085
1086 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1087 len++;
1088
1089 if (!check_space(p, end, len, opt->opt))
Simon Kelley91dccd02005-03-31 17:48:32 +01001090 return p;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001091
Simon Kelley91dccd02005-03-31 17:48:32 +01001092 *(p++) = opt->opt;
Simon Kelleycdeda282006-03-16 20:16:06 +00001093 *(p++) = len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001094
Simon Kelleycdeda282006-03-16 20:16:06 +00001095 if (len == 0)
Simon Kelley91dccd02005-03-31 17:48:32 +01001096 return p;
1097
Simon Kelleycdeda282006-03-16 20:16:06 +00001098 if (opt->flags & DHOPT_ADDR)
Simon Kelley91dccd02005-03-31 17:48:32 +01001099 {
1100 int j;
1101 struct in_addr *a = (struct in_addr *)opt->val;
1102 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1103 {
1104 /* zero means "self" (but not in vendorclass options.) */
1105 if (a->s_addr == 0)
1106 memcpy(p, &local, INADDRSZ);
1107 else
1108 memcpy(p, a, INADDRSZ);
1109 p += INADDRSZ;
1110 }
1111 }
1112 else
1113 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001114 memcpy(p, opt->val, len);
1115 p += len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001116 }
1117
1118 return p;
1119}
1120
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001121static unsigned char *do_req_options(struct dhcp_context *context,
1122 unsigned char *p, unsigned char *end,
1123 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +01001124 struct daemon *daemon,
1125 char *hostname,
Simon Kelley3be34542004-09-11 19:12:13 +01001126 struct dhcp_netid *netid,
Simon Kelley3d8df262005-08-29 12:19:27 +01001127 struct in_addr subnet_addr,
Simon Kelleycdeda282006-03-16 20:16:06 +00001128 unsigned char fqdn_flags,
1129 int null_term)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001130{
Simon Kelley3be34542004-09-11 19:12:13 +01001131 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
Simon Kelley91dccd02005-03-31 17:48:32 +01001132 char *vendor_class = NULL;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001133
Simon Kelley3be34542004-09-11 19:12:13 +01001134 /* rfc3011 says this doesn't need to be in the requested options list. */
1135 if (subnet_addr.s_addr)
1136 p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
1137
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001138 if (in_list(req_options, OPTION_NETMASK) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001139 !option_find2(netid, config_opts, OPTION_NETMASK))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001140 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
1141
Simon Kelley3be34542004-09-11 19:12:13 +01001142 /* May not have a "guessed" broadcast address if we got no packets via a relay
1143 from this net yet (ie just unicast renewals after a restart */
1144 if (context->broadcast.s_addr &&
1145 in_list(req_options, OPTION_BROADCAST) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001146 !option_find2(netid, config_opts, OPTION_BROADCAST))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001147 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
1148
Simon Kelley3be34542004-09-11 19:12:13 +01001149 /* Same comments as broadcast apply, and also may not be able to get a sensible
1150 default when using subnet select. User must configure by steam in that case. */
1151 if (context->router.s_addr &&
1152 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001153 !option_find2(netid, config_opts, OPTION_ROUTER))
Simon Kelley3be34542004-09-11 19:12:13 +01001154 p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001155
1156 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001157 !option_find2(netid, config_opts, OPTION_DNSSERVER))
Simon Kelley0a852542005-03-23 20:28:59 +00001158 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001159
Simon Kelley3be34542004-09-11 19:12:13 +01001160 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001161 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001162 p = option_put_string(p, end, OPTION_DOMAINNAME, daemon->domain_suffix, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001163
1164 /* Note that we ignore attempts to set the hostname using
Simon Kelley3d8df262005-08-29 12:19:27 +01001165 --dhcp-option=12,<name> and the fqdn using
1166 --dhc-option=81,<name> */
1167 if (hostname)
1168 {
1169 if (in_list(req_options, OPTION_HOSTNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001170 p = option_put_string(p, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01001171
1172 if (fqdn_flags != 0)
1173 {
1174 int len = strlen(hostname) + 3;
1175 if (fqdn_flags & 0x04)
1176 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00001177 else if (null_term)
1178 len++;
1179
Simon Kelley3d8df262005-08-29 12:19:27 +01001180 if (daemon->domain_suffix)
1181 len += strlen(daemon->domain_suffix) + 1;
1182
1183 if (p + len + 1 < end)
1184 {
1185 *(p++) = OPTION_CLIENT_FQDN;
1186 *(p++) = len;
1187 *(p++) = fqdn_flags;
1188 *(p++) = 255;
1189 *(p++) = 255;
1190
1191 if (fqdn_flags & 0x04)
1192 {
1193 p = do_rfc1035_name(p, hostname);
1194 if (daemon->domain_suffix)
1195 p = do_rfc1035_name(p, daemon->domain_suffix);
1196 *p++ = 0;
1197 }
1198 else
1199 {
1200 memcpy(p, hostname, strlen(hostname));
1201 p += strlen(hostname);
1202 if (daemon->domain_suffix)
1203 {
1204 *(p++) = '.';
1205 memcpy(p, daemon->domain_suffix, strlen(daemon->domain_suffix));
1206 p += strlen(daemon->domain_suffix);
Simon Kelleycdeda282006-03-16 20:16:06 +00001207 }
1208 if (null_term)
1209 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01001210 }
1211 }
1212 }
1213 }
1214
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001215 for (opt=config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001216 {
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001217 if (opt->opt == OPTION_HOSTNAME ||
Simon Kelley3d8df262005-08-29 12:19:27 +01001218 opt->opt == OPTION_CLIENT_FQDN ||
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001219 opt->opt == OPTION_MAXMESSAGE ||
1220 !in_list(req_options, opt->opt) ||
Simon Kelley91dccd02005-03-31 17:48:32 +01001221 opt != option_find2(netid, config_opts, opt->opt))
Simon Kelley33820b72004-04-03 21:10:00 +01001222 continue;
1223
1224 /* For the options we have default values on
1225 dhc-option=<optionno> means "don't include this option"
1226 not "include a zero-length option" */
1227 if (opt->len == 0 &&
1228 (opt->opt == OPTION_NETMASK ||
1229 opt->opt == OPTION_BROADCAST ||
1230 opt->opt == OPTION_ROUTER ||
1231 opt->opt == OPTION_DNSSERVER))
1232 continue;
Simon Kelley91dccd02005-03-31 17:48:32 +01001233
1234 /* opt->val has terminating zero */
1235 if (opt->opt == OPTION_VENDOR_ID)
Simon Kelley3d8df262005-08-29 12:19:27 +01001236 vendor_class = (char *)opt->val;
Simon Kelley91dccd02005-03-31 17:48:32 +01001237 else
Simon Kelleycdeda282006-03-16 20:16:06 +00001238 p = do_opt(opt, p, end, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001239 }
1240
1241 if (in_list(req_options, OPTION_VENDOR_ID))
1242 {
1243 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001244 if (match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0))
Simon Kelley91dccd02005-03-31 17:48:32 +01001245 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001246 if (vendor_class && strcmp(vendor_class, (char *)opt->vendor_class) != 0)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001247 syslog(LOG_WARNING, _("More than one vendor class matches, using %s"), vendor_class);
Simon Kelley91dccd02005-03-31 17:48:32 +01001248 else
Simon Kelley3d8df262005-08-29 12:19:27 +01001249 vendor_class = (char *)opt->vendor_class;
Simon Kelley91dccd02005-03-31 17:48:32 +01001250 }
1251
1252 if (vendor_class)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001253 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001254 p = option_put_string(p, end, OPTION_VENDOR_ID, vendor_class, 0);
Simon Kelley91dccd02005-03-31 17:48:32 +01001255
1256 if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
Simon Kelley1ab84e22004-01-29 16:48:35 +00001257 {
Simon Kelley91dccd02005-03-31 17:48:32 +01001258 unsigned char *plen, *oend = end;
1259
1260 /* encapsulated options can only be 256 bytes,
1261 even of the packet is larger */
1262 if (p + 256 < end)
1263 oend = p + 256;
1264
1265 if (p + 3 >= oend)
1266 return p;
1267
1268 *(p++) = OPTION_VENDOR_CLASS_OPT;
1269 plen = p++; /* fill in later */
1270
1271 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001272 if ((match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0)) &&
Simon Kelley3d8df262005-08-29 12:19:27 +01001273 strcmp(vendor_class, (char *)opt->vendor_class) == 0)
Simon Kelleycdeda282006-03-16 20:16:06 +00001274 p = do_opt(opt, p, oend, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001275
1276 *plen = p - plen - 1;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001277 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001278 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001279 }
1280
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001281 return p;
1282}
1283
1284