blob: abb4239291bb39e3278220677de72228ffbd18a5 [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 void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
76 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 Kelley3be34542004-09-11 19:12:13 +0100112 subnet_addr.s_addr = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100113 unsigned char *agent_id = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +0100114
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100115 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100116 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000117
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100118 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000119 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100120
Simon Kelley3be34542004-09-11 19:12:13 +0100121 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000122 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000123 {
Simon Kelley3be34542004-09-11 19:12:13 +0100124 mess_type = option_uint(opt, 1);
Simon Kelley44a2a312004-03-10 20:04:35 +0000125
Simon Kelley3be34542004-09-11 19:12:13 +0100126 /* only insist on a cookie for DHCP. */
127 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
128 return 0;
129
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100130 /* two things to note here: expand_buf may move the packet,
131 so reassign mess from daemon->packet. Also, the size
132 sent includes the IP and UDP headers, hence the magic "-28" */
133 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
134 {
135 size_t size = (size_t)option_uint(opt, 2) - 28;
136
137 if (size > DHCP_PACKET_MAX)
138 size = DHCP_PACKET_MAX;
139 else if (size < sizeof(struct dhcp_packet))
140 size = sizeof(struct dhcp_packet);
141
142 if (expand_buf(&daemon->dhcp_packet, size))
143 {
144 mess = daemon->dhcp_packet.iov_base;
145 end = ((unsigned char *)mess) + size;
146 }
147 }
148
Simon Kelley3be34542004-09-11 19:12:13 +0100149 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
150 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000151 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100152 mess->ciaddr.s_addr = 0;
153
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100154 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
155 {
156 /* Any agent-id needs to be copied back out, verbatim, as the last option
157 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
158 get overwritten, then it will be shuffled back at the end of processing.
159 Note that the incoming options must not be overwritten here, so there has to
160 be enough free space at the end of the packet to copy the option. */
161 unsigned int total = option_len(opt) + 2;
162 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
163 if (last_opt && last_opt < end - total)
164 {
165 agent_id = end - total;
166 memcpy(agent_id, opt, total);
167 }
168
169 /* look for RFC3527 Link selection sub-option */
170 if ((opt = option_find1(option_ptr(opt), option_ptr(opt) + option_len(opt), SUBOPT_SUBNET_SELECT, INADDRSZ)))
171 subnet_addr = option_addr(opt);
172 }
173
174 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
175 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100176 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000177
178 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000179 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000180 {
Simon Kelley26128d22004-11-14 16:43:54 +0000181 clid_len = option_len(opt);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000182 clid = option_ptr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000183 }
Simon Kelley0a852542005-03-23 20:28:59 +0000184
185 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100186 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000187
188 /* If this request is missing a clid, but we've seen one before,
189 use it again for option matching etc. */
190 if (lease && !clid && lease->clid)
191 {
192 clid_len = lease->clid_len;
193 clid = lease->clid;
194 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000195 }
Simon Kelley3be34542004-09-11 19:12:13 +0100196
Simon Kelleycdeda282006-03-16 20:16:06 +0000197 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
198 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100199 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
200 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000201 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100202 mac->netid.next = netid;
203 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000204 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100205
Simon Kelley0a852542005-03-23 20:28:59 +0000206 /* Determine network for this packet. Our caller will have already linked all the
207 contexts which match the addresses of the receiving interface but if the
208 machine has an address already, or came via a relay, or we have a subnet selector,
209 we search again. If we don't have have a giaddr or explicit subnet selector,
210 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100211 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
212 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000213 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
214 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100215 struct dhcp_context *context_tmp, *context_new = NULL;
216 struct in_addr addr = mess->ciaddr;
217 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000218
Simon Kelley3d8df262005-08-29 12:19:27 +0100219 if (subnet_addr.s_addr)
220 {
221 addr = subnet_addr;
222 force = 1;
223 }
224 else if (mess->giaddr.s_addr)
225 {
226 addr = mess->giaddr;
227 force = 1;
228 }
229
230 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley0a852542005-03-23 20:28:59 +0000231 if (context_tmp->netmask.s_addr &&
232 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
233 is_same_net(addr, context_tmp->end, context_tmp->netmask))
234 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100235 context_tmp->current = context_new;
236 context_new = context_tmp;
Simon Kelley0a852542005-03-23 20:28:59 +0000237 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100238
239 if (context_new || force)
240 context = context_new;
241
Simon Kelley0a852542005-03-23 20:28:59 +0000242 }
Simon Kelley3be34542004-09-11 19:12:13 +0100243
244 if (!context)
245 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000246 syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"),
247 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
Simon Kelley0a852542005-03-23 20:28:59 +0000248 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 +0100249 return 0;
250 }
251
Simon Kelleyaedef832006-01-22 14:02:31 +0000252 /* keep _a_ local address available. */
253 fallback = context->local;
254
Simon Kelley3be34542004-09-11 19:12:13 +0100255 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100256 p = mess->options + sizeof(u32); /* skip cookie */
257
Simon Kelleycdeda282006-03-16 20:16:06 +0000258 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
259 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley26128d22004-11-14 16:43:54 +0000260
Simon Kelley3be34542004-09-11 19:12:13 +0100261 if (mess_type == 0)
262 {
263 /* BOOTP request */
Simon Kelley26128d22004-11-14 16:43:54 +0000264 struct dhcp_netid id;
265 char save = mess->file[128];
266 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100267
268 /* must have a MAC addr for bootp */
269 if (mess->htype == 0 || mess->hlen == 0)
270 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000271
Simon Kelley26128d22004-11-14 16:43:54 +0000272 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000273 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000274
275 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
276
277 if (have_config(config, CONFIG_NAME))
278 hostname = config->hostname;
279
280 if (have_config(config, CONFIG_NETID))
281 {
282 config->netid.next = netid;
283 netid = &config->netid;
284 }
285
286 /* Match incoming filename field as a netid. */
287 if (mess->file[0])
288 {
289 mess->file[128] = 0; /* ensure zero term. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100290 id.net = (char *)mess->file;
Simon Kelley26128d22004-11-14 16:43:54 +0000291 id.next = netid;
292 netid = &id;
293 }
294
295 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000296 if (match_netid(id_list->list, netid, 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000297 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000298
Simon Kelley3d8df262005-08-29 12:19:27 +0100299 if (!message)
300 {
301 if (have_config(config, CONFIG_ADDR))
302 {
303 logaddr = &config->addr;
304 mess->yiaddr = config->addr;
305 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000306 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100307 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000308 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000309 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100310 }
311 else if (!(daemon->options & OPT_BOOTP_DYNAMIC))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000312 message = _("no address configured");
Simon Kelley3d8df262005-08-29 12:19:27 +0100313 else
314 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000315 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelleye17fb622006-01-14 20:33:46 +0000316 !address_available(context, lease->addr))
317 {
318 if (lease)
319 {
320 /* lease exists, wrong network. */
321 lease_prune(lease, now);
322 lease = NULL;
323 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000324 if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000325 message = _("no address available");
326 }
327 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100328 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100329 }
330
Simon Kelley7cebd202006-05-06 14:13:33 +0100331 if (!message &&
332 !lease &&
333 (!(lease = lease_allocate(mess->yiaddr))))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000334 message = _("no leases left");
Simon Kelley3d8df262005-08-29 12:19:27 +0100335
Simon Kelleye17fb622006-01-14 20:33:46 +0000336 if (!message && !(context = narrow_context(context, mess->yiaddr)))
337 message = _("wrong network");
338
Simon Kelley3d8df262005-08-29 12:19:27 +0100339 if (!message)
340 {
341 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100342
Simon Kelleycdeda282006-03-16 20:16:06 +0000343 if (context->netid.net)
Simon Kelley3d8df262005-08-29 12:19:27 +0100344 {
345 context->netid.next = netid;
346 netid = &context->netid;
347 }
348
Simon Kelleycdeda282006-03-16 20:16:06 +0000349 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100350 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000351 lease_set_hostname(lease, hostname, daemon->domain_suffix, 1);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100352 lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
Simon Kelley3d8df262005-08-29 12:19:27 +0100353
354 p = do_req_options(context, p, end, NULL, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000355 hostname, netid, subnet_addr, fqdn_flags, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100356 /* must do this after do_req_options since it overwrites filename field. */
357 mess->siaddr = context->local;
358 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100359 p = option_end(p, end, NULL, mess);
Simon Kelley3d8df262005-08-29 12:19:27 +0100360 }
361 }
362
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100363 log_packet(daemon, NULL, logaddr, mess, iface_name, message);
Simon Kelley26128d22004-11-14 16:43:54 +0000364 mess->file[128] = save;
365
366 if (message)
367 return 0;
368 else
369 return p - (unsigned char *)mess;
Simon Kelley3be34542004-09-11 19:12:13 +0100370 }
371
Simon Kelley3d8df262005-08-29 12:19:27 +0100372 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
373 {
374 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
375 int len = option_len(opt);
376 char *pq = daemon->dhcp_buff;
377 unsigned char *pp, *op = option_ptr(opt);
378
379 fqdn_flags = *op;
380 len -= 3;
381 op += 3;
382 pp = op;
383
384 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000385 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100386 fqdn_flags |= 0x02;
387
388 fqdn_flags &= ~0x08;
389 fqdn_flags |= 0x01;
390
391 if (fqdn_flags & 0x04)
392 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
393 {
394 memcpy(pq, op+1, *op);
395 pq += *op;
396 op += (*op)+1;
397 *(pq++) = '.';
398 }
399 else
400 {
401 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000402 if (len > 0 && op[len-1] == 0)
403 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100404 pq += len + 1;
405 }
406
407 if (pq != daemon->dhcp_buff)
408 pq--;
409
410 *pq = 0;
411
412 if (canonicalise(daemon->dhcp_buff))
413 offer_hostname = client_hostname = daemon->dhcp_buff;
414 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000415 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000417 int len = option_len(opt);
Simon Kelley3d8df262005-08-29 12:19:27 +0100418 memcpy(daemon->dhcp_buff, option_ptr(opt), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000419 /* Microsoft clients are broken, and need zero-terminated strings
420 in options. We detect this state here, and do the same in
421 any options we send */
422 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
423 borken_opt = 1;
424 else
425 daemon->dhcp_buff[len] = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100426 if (canonicalise(daemon->dhcp_buff))
427 client_hostname = daemon->dhcp_buff;
428 }
429
430 if (have_config(config, CONFIG_NAME))
431 {
432 hostname = config->hostname;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000433 hostname_auth = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100434 /* be careful not to send an OFFER with a hostname not
435 matching the DISCOVER. */
436 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
437 offer_hostname = hostname;
438 }
439 else if (client_hostname && (hostname = strip_hostname(daemon, client_hostname)) && !config)
440 {
441 /* Search again now we have a hostname.
442 Only accept configs without CLID and HWADDR here, (they won't match)
443 to avoid impersonation by name. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000444 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
445 mess->chaddr, mess->hlen,
446 mess->htype, hostname);
Simon Kelley3d8df262005-08-29 12:19:27 +0100447 if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
448 config = new;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000449 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100450
Simon Kelleya2226412004-05-13 20:27:08 +0100451 if (have_config(config, CONFIG_NETID))
452 {
453 config->netid.next = netid;
454 netid = &config->netid;
455 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100456
Simon Kelley26128d22004-11-14 16:43:54 +0000457 /* user-class options are, according to RFC3004, supposed to contain
458 a set of counted strings. Here we check that this is so (by seeing
459 if the counts are consistent with the overall option length) and if
460 so zero the counts so that we don't get spurious matches between
461 the vendor string and the counts. If the lengths don't add up, we
462 assume that the option is a single string and non RFC3004 compliant
463 and just do the substring match. dhclient provides these broken options. */
Simon Kelleya2226412004-05-13 20:27:08 +0100464
Simon Kelleybb01cb92004-12-13 20:56:23 +0000465 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100466 {
Simon Kelley26128d22004-11-14 16:43:54 +0000467 unsigned char *ucp = option_ptr(opt);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000468 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000469 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
470 if (j == option_len(opt))
471 for (j = 0; j < option_len(opt); j = tmp)
472 {
473 tmp = j + ucp[j] + 1;
474 ucp[j] = 0;
475 }
Simon Kelleya2226412004-05-13 20:27:08 +0100476 }
Simon Kelley26128d22004-11-14 16:43:54 +0000477
478 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleybb01cb92004-12-13 20:56:23 +0000479 if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000480 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000481 int i;
Simon Kelley26128d22004-11-14 16:43:54 +0000482 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
483 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
484 {
485 vendor->netid.next = netid;
486 netid = &vendor->netid;
487 break;
488 }
489 }
Simon Kelleycdeda282006-03-16 20:16:06 +0000490
Simon Kelley26128d22004-11-14 16:43:54 +0000491 /* if all the netids in the ignore list are present, ignore this client */
492 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000493 if (match_netid(id_list->list, netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000494 ignore = 1;
495
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100496 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
497 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000498 clid = NULL;
499
Simon Kelleybb01cb92004-12-13 20:56:23 +0000500 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100501 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100502 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000503 memcpy(req_options, option_ptr(opt), option_len(opt));
504 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100505 }
506
Simon Kelley3be34542004-09-11 19:12:13 +0100507 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000508 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000509 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000510 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000511 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000512 return 0;
513
514 /* sanitise any message. Paranoid? Moi? */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000515 if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000516 {
Simon Kelley3be34542004-09-11 19:12:13 +0100517 char *p = option_ptr(opt), *q = daemon->dhcp_buff;
Simon Kelley44a2a312004-03-10 20:04:35 +0000518 int i;
519
520 for (i = option_len(opt); i > 0; i--)
521 {
522 char c = *p++;
523 if (isprint(c))
524 *q++ = c;
525 }
526 *q++ = 0; /* add terminator */
Simon Kelley3be34542004-09-11 19:12:13 +0100527 message = daemon->dhcp_buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000528 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000529
Simon Kelleybb01cb92004-12-13 20:56:23 +0000530 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000531 return 0;
532
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100533 log_packet(daemon, "DECLINE", option_ptr(opt), mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000534
535 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
536 lease_prune(lease, now);
537
Simon Kelley33820b72004-04-03 21:10:00 +0100538 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000539 config->addr.s_addr == option_addr(opt).s_addr)
540 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000541 syslog(LOG_WARNING, _("disabling DHCP static address %s"), inet_ntoa(config->addr));
Simon Kelley33820b72004-04-03 21:10:00 +0100542 config->flags &= ~CONFIG_ADDR ;
Simon Kelley44a2a312004-03-10 20:04:35 +0000543 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100544 else
545 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100546 for (; context; context = context->current)
547 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000548
549 return 0;
550
551 case DHCPRELEASE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000552 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000553 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000554 return 0;
555
Simon Kelley44a2a312004-03-10 20:04:35 +0000556 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
557 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100558 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000559 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100560
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100561 log_packet(daemon, "RELEASE", &mess->ciaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000562
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000563 return 0;
564
565 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100566 if (ignore || have_config(config, CONFIG_DISABLE))
567 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000568 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100569 opt = NULL;
570 }
571 else
572 {
573 struct in_addr addr, conf;
574
575 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
576 addr = option_addr(opt);
577
578 conf.s_addr = 0;
579 if (have_config(config, CONFIG_ADDR))
580 {
581 if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease)
582 syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
583 inet_ntoa(config->addr), print_mac(daemon, ltmp->hwaddr, ltmp->hwaddr_len));
584 else
585 {
586 struct dhcp_context *tmp;
587 for (tmp = context; tmp; tmp = tmp->current)
588 if (context->local.s_addr == config->addr.s_addr)
589 break;
590 if (tmp)
591 syslog(LOG_WARNING, _("not using configured address %s because it is in use by the server"),
592 inet_ntoa(config->addr));
593 else
594 conf = config->addr;
595 }
596 }
597
598 if (conf.s_addr)
599 mess->yiaddr = conf;
600 else if (lease && address_available(context, lease->addr))
601 mess->yiaddr = lease->addr;
602 else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
603 !config_find_by_address(daemon->dhcp_conf, addr))
604 mess->yiaddr = addr;
605 else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
606 message = _("no address available");
607 }
608
609 log_packet(daemon, "DISCOVER", opt ? (struct in_addr *)option_ptr(opt) : NULL, mess, iface_name, message);
Simon Kelley3d8df262005-08-29 12:19:27 +0100610
Simon Kelleye17fb622006-01-14 20:33:46 +0000611 if (message || !(context = narrow_context(context, mess->yiaddr)))
Simon Kelley33820b72004-04-03 21:10:00 +0100612 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +0000613
Simon Kelleycdeda282006-03-16 20:16:06 +0000614 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000615 {
616 context->netid.next = netid;
617 netid = &context->netid;
618 }
619
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100620 time = calc_time(context, config, lease, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelley0a852542005-03-23 20:28:59 +0000621 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000622 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000623 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley0a852542005-03-23 20:28:59 +0000624 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000625 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100626 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +0000627 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100628 {
Simon Kelley59353a62004-11-21 19:34:28 +0000629 p = option_put(p, end, OPTION_T1, 4, (time/2));
630 p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100631 }
Simon Kelley3be34542004-09-11 19:12:13 +0100632 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000633 offer_hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100634 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000635
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636 log_packet(daemon, "OFFER" , &mess->yiaddr, mess, iface_name, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000637 return p - (unsigned char *)mess;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000638
639 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +0000640 if (ignore || have_config(config, CONFIG_DISABLE))
641 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000642 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000643 {
644 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000645 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000646
Simon Kelleybb01cb92004-12-13 20:56:23 +0000647 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000648 {
Simon Kelley3be34542004-09-11 19:12:13 +0100649 /* SELECTING */
Simon Kelleye17fb622006-01-14 20:33:46 +0000650 for (; context; context = context->current)
651 if (context->local.s_addr == option_addr(opt).s_addr)
652 break;
653
654 if (!context)
Simon Kelley3be34542004-09-11 19:12:13 +0100655 return 0;
656
657 /* If a lease exists for this host and another address, squash it. */
658 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
659 {
660 lease_prune(lease, now);
661 lease = NULL;
662 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000663 }
Simon Kelley3be34542004-09-11 19:12:13 +0100664 else
665 {
666 /* INIT-REBOOT */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100667 if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +0100668 return 0;
669
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100670 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000671 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +0100672 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000673 }
674 else
675 {
676 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +0000677 /* Check existing lease for this address.
678 We allow it to be missing if dhcp-authoritative mode
679 as long as we can allocate the lease now - checked below.
680 This makes for a smooth recovery from a lost lease DB */
681 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
682 (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000683 {
684 message = _("lease not found");
685 /* ensure we broadcast NAK */
686 unicast_dest = 0;
687 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100688 /* desynchronise renewals */
689 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +0100690 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000691 }
692
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100693 log_packet(daemon, "REQUEST", &mess->yiaddr, mess, iface_name, NULL);
Simon Kelleye17fb622006-01-14 20:33:46 +0000694
Simon Kelleydfa666f2004-08-02 18:27:27 +0100695 if (!message)
696 {
697 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100698 struct dhcp_context *tmp = NULL;
699
700 if (have_config(config, CONFIG_ADDR))
701 for (tmp = context; tmp; tmp = tmp->current)
702 if (context->local.s_addr == config->addr.s_addr)
703 break;
Simon Kelleyaedef832006-01-22 14:02:31 +0000704
Simon Kelleye17fb622006-01-14 20:33:46 +0000705 if (!(context = narrow_context(context, mess->yiaddr)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000706 {
Simon Kelleye17fb622006-01-14 20:33:46 +0000707 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000708 message = _("wrong network");
709 /* ensure we broadcast NAK */
710 unicast_dest = 0;
711 }
712
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100713 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelleydfa666f2004-08-02 18:27:27 +0100714 else if (!address_available(context, mess->yiaddr) &&
715 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000716 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +0000717
Simon Kelleydfa666f2004-08-02 18:27:27 +0100718 /* Check if a new static address has been configured. Be very sure that
719 when the client does DISCOVER, it will get the static address, otherwise
720 an endless protocol loop will ensue. */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100721 else if (!tmp &&
722 have_config(config, CONFIG_ADDR) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100723 config->addr.s_addr != mess->yiaddr.s_addr &&
724 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000725 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100726
727 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100728 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000729 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100730
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100731 else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000732 message = _("address in use");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100733
Simon Kelley7cebd202006-05-06 14:13:33 +0100734 else if (!clid && mess->hlen == 0)
735 message = _("no unique-id");
736
737 else if (!lease &&
738 !(lease = lease_allocate(mess->yiaddr)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000739 message = _("no leases left");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100740 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000741
742 if (message)
743 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100744 log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000745
Simon Kelleyb8187c82005-11-26 21:46:27 +0000746 mess->siaddr.s_addr = mess->yiaddr.s_addr = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000747 bootp_option_put(mess, NULL, NULL);
748 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelleyaedef832006-01-22 14:02:31 +0000749 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
750 ntohl(context ? context->local.s_addr : fallback.s_addr));
Simon Kelleycdeda282006-03-16 20:16:06 +0000751 p = option_put_string(p, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000752 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
753 a distant subnet which unicast a REQ to us won't work. */
754 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
755 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
756 {
757 mess->flags |= htons(0x8000); /* broadcast */
758 mess->ciaddr.s_addr = 0;
759 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000760 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100761 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000762 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000763 if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr)))
764 {
765 hostname = client_hostname;
766 hostname_auth = 1;
767 }
768
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100769 log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100770
Simon Kelleycdeda282006-03-16 20:16:06 +0000771 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000772 {
773 context->netid.next = netid;
774 netid = &context->netid;
775 }
776
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100777 time = calc_time(context, config, NULL, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
Simon Kelleycdeda282006-03-16 20:16:06 +0000778 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100779 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000780 lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100781 lease_set_expires(lease, time, now);
Simon Kelley0a852542005-03-23 20:28:59 +0000782
783 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000784 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100785 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000786 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley59353a62004-11-21 19:34:28 +0000787 p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
788 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100789 {
Simon Kelley59353a62004-11-21 19:34:28 +0000790 while (fuzz > (time/16))
791 fuzz = fuzz/2;
792 p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100793 p = option_put(p, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100794 }
795 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000796 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000797 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100798
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100799 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000800 return p - (unsigned char *)mess;
801
802 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +0000803 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000804 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +0100805
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100806 log_packet(daemon, "INFORM", &mess->ciaddr, mess, iface_name, message);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100807
Simon Kelleye17fb622006-01-14 20:33:46 +0000808 if (message || mess->ciaddr.s_addr == 0 ||
809 !(context = narrow_context(context, mess->ciaddr)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100810 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000811
Simon Kelley59353a62004-11-21 19:34:28 +0000812 if (context->netid.net)
813 {
814 context->netid.next = netid;
815 netid = &context->netid;
816 }
817
Simon Kelley0a852542005-03-23 20:28:59 +0000818 mess->siaddr = context->local;
Simon Kelley26128d22004-11-14 16:43:54 +0000819 bootp_option_put(mess, daemon->boot_config, netid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000820 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley0a852542005-03-23 20:28:59 +0000821 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000822 if (!hostname)
823 hostname = host_from_dns(daemon, mess->yiaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100824 p = do_req_options(context, p, end, req_options, daemon,
Simon Kelleycdeda282006-03-16 20:16:06 +0000825 hostname, netid, subnet_addr, fqdn_flags, borken_opt);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100826 p = option_end(p, end, agent_id, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000827
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100828 log_packet(daemon, "ACK", &mess->ciaddr, mess, iface_name, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000829 return p - (unsigned char *)mess;
830 }
831
832 return 0;
833}
834
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100835static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config,
836 struct dhcp_lease *lease, unsigned char *opt, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000837{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100838 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +0000839
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100840 if (opt)
841 {
842 unsigned int req_time = option_uint(opt, 4);
843 if (req_time < 120 )
844 req_time = 120; /* sanity */
845 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
846 time = req_time;
847 }
848 else if (lease && lease->expires != 0 && difftime(lease->expires, now) > 0.0)
849 {
850 unsigned int lease_time = (unsigned int)difftime(lease->expires, now);
851
852 /* put a floor on lease-remaining time. */
853 if (lease_time < 360 )
854 lease_time = 360;
855
856 if (time > lease_time)
857 time = lease_time;
858 }
859
860 return time;
861}
862
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100863static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
864 struct dhcp_packet *mess, char *interface, char *string)
865{
Simon Kelley7cebd202006-05-06 14:13:33 +0100866 syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
Simon Kelley3be34542004-09-11 19:12:13 +0100867 type ? "DHCP" : "BOOTP",
868 type ? type : "",
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000869 interface,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000870 addr ? inet_ntoa(*addr) : "",
Simon Kelleycdeda282006-03-16 20:16:06 +0000871 addr ? " " : "",
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100872 print_mac(daemon, mess->chaddr, mess->hlen),
Simon Kelley44a2a312004-03-10 20:04:35 +0000873 string ? string : "");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000874}
875
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000876static struct in_addr option_addr(unsigned char *opt)
877{
878 /* this worries about unaligned data in the option. */
879 /* struct in_addr is network byte order */
880 struct in_addr ret;
881
882 memcpy(&ret, option_ptr(opt), INADDRSZ);
883
884 return ret;
885}
886
Simon Kelley44a2a312004-03-10 20:04:35 +0000887static unsigned int option_uint(unsigned char *opt, int size)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000888{
889 /* this worries about unaligned data and byte order */
Simon Kelley44a2a312004-03-10 20:04:35 +0000890 unsigned int ret = 0;
891 int i;
892 unsigned char *p = option_ptr(opt);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000893
Simon Kelley44a2a312004-03-10 20:04:35 +0000894 for (i = 0; i < size; i++)
895 ret = (ret << 8) | *p++;
896
897 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000898}
899
Simon Kelley26128d22004-11-14 16:43:54 +0000900static void bootp_option_put(struct dhcp_packet *mess,
901 struct dhcp_boot *boot_opts, struct dhcp_netid *netids)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000902{
Simon Kelley26128d22004-11-14 16:43:54 +0000903 struct dhcp_boot *tmp;
904
905 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000906 if (match_netid(tmp->netid, netids, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000907 break;
908 if (!tmp)
909 /* No match, look for one without a netid */
910 for (tmp = boot_opts; tmp; tmp = tmp->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000911 if (match_netid(tmp->netid, netids, 1))
Simon Kelley26128d22004-11-14 16:43:54 +0000912 break;
913
914 /* Do this _after_ the matching above, since in
915 BOOTP mode, one if the things we match is the filename. */
916
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000917 memset(mess->sname, 0, sizeof(mess->sname));
918 memset(mess->file, 0, sizeof(mess->file));
Simon Kelley26128d22004-11-14 16:43:54 +0000919
920 if (tmp)
921 {
922 if (tmp->sname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100923 strncpy((char *)mess->sname, tmp->sname, sizeof(mess->sname)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000924 if (tmp->file)
Simon Kelley3d8df262005-08-29 12:19:27 +0100925 strncpy((char *)mess->file, tmp->file, sizeof(mess->file)-1);
Simon Kelley26128d22004-11-14 16:43:54 +0000926 if (tmp->next_server.s_addr)
927 mess->siaddr = tmp->next_server;
928 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000929}
930
Simon Kelleyb8187c82005-11-26 21:46:27 +0000931static int check_space(unsigned char *p, unsigned char *end, int len, int opt)
932{
933 /* always keep one octet space for the END option. */
934 if (p + len + 3 >= end)
935 {
936 syslog(LOG_WARNING, _("cannot send DHCP option %d: no space left in packet"), opt);
937 return 0;
938 }
939
940 return 1;
941}
942
943
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000944static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
945{
946 int i;
Simon Kelley44a2a312004-03-10 20:04:35 +0000947
Simon Kelleyb8187c82005-11-26 21:46:27 +0000948 if (check_space(p, end, len, opt))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000949 {
950 *(p++) = opt;
Simon Kelleya2226412004-05-13 20:27:08 +0100951 *(p++) = len;
952
953 for (i = 0; i < len; i++)
954 *(p++) = val >> (8 * (len - (i + 1)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000955 }
956 return p;
957}
958
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100959static unsigned char *option_end(unsigned char *p, unsigned char *end,
960 unsigned char *agent_id, struct dhcp_packet *start)
Simon Kelleya2226412004-05-13 20:27:08 +0100961{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100962 /* shuffle agent-id back down if we have room for it */
963 if (agent_id && p < agent_id)
964 {
965 memmove(p, agent_id, end - agent_id);
966 p += end - agent_id;
967 }
968
Simon Kelleya2226412004-05-13 20:27:08 +0100969 *(p++) = OPTION_END;
970 while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
971 *p++ = 0;
972
973 return p;
974}
975
Simon Kelleycdeda282006-03-16 20:16:06 +0000976static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt,
977 char *string, int null_term)
Simon Kelley44a2a312004-03-10 20:04:35 +0000978{
Simon Kelley0a852542005-03-23 20:28:59 +0000979 size_t len = strlen(string);
Simon Kelley3be34542004-09-11 19:12:13 +0100980
Simon Kelleycdeda282006-03-16 20:16:06 +0000981 if (null_term && len != 255)
982 len++;
983
Simon Kelleyb8187c82005-11-26 21:46:27 +0000984 if (check_space(p, end, len, opt))
Simon Kelley44a2a312004-03-10 20:04:35 +0000985 {
986 *(p++) = opt;
Simon Kelley3be34542004-09-11 19:12:13 +0100987 *(p++) = len;
988 memcpy(p, string, len);
989 p += len;
Simon Kelley44a2a312004-03-10 20:04:35 +0000990 }
Simon Kelley3be34542004-09-11 19:12:13 +0100991
Simon Kelley44a2a312004-03-10 20:04:35 +0000992 return p;
993}
994
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100995static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000996{
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000997 while (*p != OPTION_END)
998 {
Simon Kelleybb01cb92004-12-13 20:56:23 +0000999 if (p >= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001000 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001001 else if (*p == OPTION_PAD)
1002 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001003 else
1004 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001005 int opt_len;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001006 if (p >= end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001007 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001008 opt_len = option_len(p);
Simon Kelleybb01cb92004-12-13 20:56:23 +00001009 if (p >= end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001010 return NULL; /* malformed packet */
1011 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001012 return p;
1013 p += opt_len + 2;
1014 }
1015 }
1016
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001017 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001018}
1019
Simon Kelleycdeda282006-03-16 20:16:06 +00001020static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001021{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001022 unsigned char *ret, *opt;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001023
Simon Kelley3be34542004-09-11 19:12:13 +01001024 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001025 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1026 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001027
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001028 /* look for overload option. */
1029 if (!(opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
1030 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001031
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001032 /* Can we look in filename area ? */
1033 if ((option_uint(opt, 1) & 1) &&
1034 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1035 return ret;
1036
1037 /* finally try sname area */
1038 if ((option_uint(opt, 1) & 2) &&
1039 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1040 return ret;
1041
1042 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001043}
1044
1045static int in_list(unsigned char *list, int opt)
1046{
1047 int i;
1048
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001049 /* If no requested options, send everything, not nothing. */
1050 if (!list)
1051 return 1;
1052
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001053 for (i = 0; list[i] != OPTION_END; i++)
1054 if (opt == list[i])
1055 return 1;
1056
1057 return 0;
1058}
1059
Simon Kelleya2226412004-05-13 20:27:08 +01001060static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001061{
Simon Kelley91dccd02005-03-31 17:48:32 +01001062 struct dhcp_opt *tmp;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001063 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelleya2226412004-05-13 20:27:08 +01001064 if (tmp->opt == opt)
Simon Kelleycdeda282006-03-16 20:16:06 +00001065 if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
1066 return tmp;
Simon Kelleya2226412004-05-13 20:27:08 +01001067
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001068 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001069}
1070
Simon Kelleycdeda282006-03-16 20:16:06 +00001071static unsigned char *do_opt(struct dhcp_opt *opt, unsigned char *p, unsigned char *end,
1072 struct in_addr local, int null_term)
Simon Kelley91dccd02005-03-31 17:48:32 +01001073{
Simon Kelleycdeda282006-03-16 20:16:06 +00001074 int len = opt->len;
1075
1076 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1077 len++;
1078
1079 if (!check_space(p, end, len, opt->opt))
Simon Kelley91dccd02005-03-31 17:48:32 +01001080 return p;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001081
Simon Kelley91dccd02005-03-31 17:48:32 +01001082 *(p++) = opt->opt;
Simon Kelleycdeda282006-03-16 20:16:06 +00001083 *(p++) = len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001084
Simon Kelleycdeda282006-03-16 20:16:06 +00001085 if (len == 0)
Simon Kelley91dccd02005-03-31 17:48:32 +01001086 return p;
1087
Simon Kelleycdeda282006-03-16 20:16:06 +00001088 if (opt->flags & DHOPT_ADDR)
Simon Kelley91dccd02005-03-31 17:48:32 +01001089 {
1090 int j;
1091 struct in_addr *a = (struct in_addr *)opt->val;
1092 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1093 {
1094 /* zero means "self" (but not in vendorclass options.) */
1095 if (a->s_addr == 0)
1096 memcpy(p, &local, INADDRSZ);
1097 else
1098 memcpy(p, a, INADDRSZ);
1099 p += INADDRSZ;
1100 }
1101 }
1102 else
1103 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001104 memcpy(p, opt->val, len);
1105 p += len;
Simon Kelley91dccd02005-03-31 17:48:32 +01001106 }
1107
1108 return p;
1109}
1110
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001111static unsigned char *do_req_options(struct dhcp_context *context,
1112 unsigned char *p, unsigned char *end,
1113 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +01001114 struct daemon *daemon,
1115 char *hostname,
Simon Kelley3be34542004-09-11 19:12:13 +01001116 struct dhcp_netid *netid,
Simon Kelley3d8df262005-08-29 12:19:27 +01001117 struct in_addr subnet_addr,
Simon Kelleycdeda282006-03-16 20:16:06 +00001118 unsigned char fqdn_flags,
1119 int null_term)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001120{
Simon Kelley3be34542004-09-11 19:12:13 +01001121 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
Simon Kelley91dccd02005-03-31 17:48:32 +01001122 char *vendor_class = NULL;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001123
Simon Kelley3be34542004-09-11 19:12:13 +01001124 /* rfc3011 says this doesn't need to be in the requested options list. */
1125 if (subnet_addr.s_addr)
1126 p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
1127
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001128 if (in_list(req_options, OPTION_NETMASK) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001129 !option_find2(netid, config_opts, OPTION_NETMASK))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001130 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
1131
Simon Kelley3be34542004-09-11 19:12:13 +01001132 /* May not have a "guessed" broadcast address if we got no packets via a relay
1133 from this net yet (ie just unicast renewals after a restart */
1134 if (context->broadcast.s_addr &&
1135 in_list(req_options, OPTION_BROADCAST) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001136 !option_find2(netid, config_opts, OPTION_BROADCAST))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001137 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
1138
Simon Kelley3be34542004-09-11 19:12:13 +01001139 /* Same comments as broadcast apply, and also may not be able to get a sensible
1140 default when using subnet select. User must configure by steam in that case. */
1141 if (context->router.s_addr &&
1142 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001143 !option_find2(netid, config_opts, OPTION_ROUTER))
Simon Kelley3be34542004-09-11 19:12:13 +01001144 p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001145
1146 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001147 !option_find2(netid, config_opts, OPTION_DNSSERVER))
Simon Kelley0a852542005-03-23 20:28:59 +00001148 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001149
Simon Kelley3be34542004-09-11 19:12:13 +01001150 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001151 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001152 p = option_put_string(p, end, OPTION_DOMAINNAME, daemon->domain_suffix, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001153
1154 /* Note that we ignore attempts to set the hostname using
Simon Kelley3d8df262005-08-29 12:19:27 +01001155 --dhcp-option=12,<name> and the fqdn using
1156 --dhc-option=81,<name> */
1157 if (hostname)
1158 {
1159 if (in_list(req_options, OPTION_HOSTNAME))
Simon Kelleycdeda282006-03-16 20:16:06 +00001160 p = option_put_string(p, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01001161
1162 if (fqdn_flags != 0)
1163 {
1164 int len = strlen(hostname) + 3;
1165 if (fqdn_flags & 0x04)
1166 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00001167 else if (null_term)
1168 len++;
1169
Simon Kelley3d8df262005-08-29 12:19:27 +01001170 if (daemon->domain_suffix)
1171 len += strlen(daemon->domain_suffix) + 1;
1172
1173 if (p + len + 1 < end)
1174 {
1175 *(p++) = OPTION_CLIENT_FQDN;
1176 *(p++) = len;
1177 *(p++) = fqdn_flags;
1178 *(p++) = 255;
1179 *(p++) = 255;
1180
1181 if (fqdn_flags & 0x04)
1182 {
1183 p = do_rfc1035_name(p, hostname);
1184 if (daemon->domain_suffix)
1185 p = do_rfc1035_name(p, daemon->domain_suffix);
1186 *p++ = 0;
1187 }
1188 else
1189 {
1190 memcpy(p, hostname, strlen(hostname));
1191 p += strlen(hostname);
1192 if (daemon->domain_suffix)
1193 {
1194 *(p++) = '.';
1195 memcpy(p, daemon->domain_suffix, strlen(daemon->domain_suffix));
1196 p += strlen(daemon->domain_suffix);
Simon Kelleycdeda282006-03-16 20:16:06 +00001197 }
1198 if (null_term)
1199 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01001200 }
1201 }
1202 }
1203 }
1204
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001205 for (opt=config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001206 {
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001207 if (opt->opt == OPTION_HOSTNAME ||
Simon Kelley3d8df262005-08-29 12:19:27 +01001208 opt->opt == OPTION_CLIENT_FQDN ||
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001209 opt->opt == OPTION_MAXMESSAGE ||
1210 !in_list(req_options, opt->opt) ||
Simon Kelley91dccd02005-03-31 17:48:32 +01001211 opt != option_find2(netid, config_opts, opt->opt))
Simon Kelley33820b72004-04-03 21:10:00 +01001212 continue;
1213
1214 /* For the options we have default values on
1215 dhc-option=<optionno> means "don't include this option"
1216 not "include a zero-length option" */
1217 if (opt->len == 0 &&
1218 (opt->opt == OPTION_NETMASK ||
1219 opt->opt == OPTION_BROADCAST ||
1220 opt->opt == OPTION_ROUTER ||
1221 opt->opt == OPTION_DNSSERVER))
1222 continue;
Simon Kelley91dccd02005-03-31 17:48:32 +01001223
1224 /* opt->val has terminating zero */
1225 if (opt->opt == OPTION_VENDOR_ID)
Simon Kelley3d8df262005-08-29 12:19:27 +01001226 vendor_class = (char *)opt->val;
Simon Kelley91dccd02005-03-31 17:48:32 +01001227 else
Simon Kelleycdeda282006-03-16 20:16:06 +00001228 p = do_opt(opt, p, end, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001229 }
1230
1231 if (in_list(req_options, OPTION_VENDOR_ID))
1232 {
1233 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001234 if (match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0))
Simon Kelley91dccd02005-03-31 17:48:32 +01001235 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001236 if (vendor_class && strcmp(vendor_class, (char *)opt->vendor_class) != 0)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001237 syslog(LOG_WARNING, _("More than one vendor class matches, using %s"), vendor_class);
Simon Kelley91dccd02005-03-31 17:48:32 +01001238 else
Simon Kelley3d8df262005-08-29 12:19:27 +01001239 vendor_class = (char *)opt->vendor_class;
Simon Kelley91dccd02005-03-31 17:48:32 +01001240 }
1241
1242 if (vendor_class)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001243 {
Simon Kelleycdeda282006-03-16 20:16:06 +00001244 p = option_put_string(p, end, OPTION_VENDOR_ID, vendor_class, 0);
Simon Kelley91dccd02005-03-31 17:48:32 +01001245
1246 if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
Simon Kelley1ab84e22004-01-29 16:48:35 +00001247 {
Simon Kelley91dccd02005-03-31 17:48:32 +01001248 unsigned char *plen, *oend = end;
1249
1250 /* encapsulated options can only be 256 bytes,
1251 even of the packet is larger */
1252 if (p + 256 < end)
1253 oend = p + 256;
1254
1255 if (p + 3 >= oend)
1256 return p;
1257
1258 *(p++) = OPTION_VENDOR_CLASS_OPT;
1259 plen = p++; /* fill in later */
1260
1261 for (opt = daemon->vendor_opts; opt; opt = opt->next)
Simon Kelleycdeda282006-03-16 20:16:06 +00001262 if ((match_netid(opt->netid, netid, 1) || match_netid(opt->netid, netid, 0)) &&
Simon Kelley3d8df262005-08-29 12:19:27 +01001263 strcmp(vendor_class, (char *)opt->vendor_class) == 0)
Simon Kelleycdeda282006-03-16 20:16:06 +00001264 p = do_opt(opt, p, oend, context->local, null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001265
1266 *plen = p - plen - 1;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001267 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001268 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001269 }
1270
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001271 return p;
1272}
1273
1274