blob: b96b1974515537e99b34ce749dac914f95c2a867 [file] [log] [blame]
Simon Kelley28866e92011-02-14 20:19:14 +00001/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley7622fc02009-06-04 20:32:05 +010019#ifdef HAVE_DHCP
20
Simon Kelley0a852542005-03-23 20:28:59 +000021#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010022#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010023#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000024
Simon Kelley316e2732010-01-22 20:16:09 +000025#ifdef HAVE_SCRIPT
26static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim);
27static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
28#endif
Simon Kelley572b41e2011-02-18 18:11:18 +000029
Simon Kelley316e2732010-01-22 20:16:09 +000030static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
Simon Kelleyf2621c72007-04-29 19:47:21 +010031static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000032static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000033static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000034static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
35static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
36 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000037static struct in_addr option_addr(unsigned char *opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010038static struct in_addr option_addr_arr(unsigned char *opt, int offset);
39static unsigned int option_uint(unsigned char *opt, int i, int size);
40static void log_packet(char *type, void *addr, unsigned char *ext_mac,
41 int mac_len, char *interface, char *string, u32 xid);
Simon Kelleycdeda282006-03-16 20:16:06 +000042static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010043static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley7de060b2011-08-26 17:24:52 +010044static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess);
45static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +010046static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000047static void do_options(struct dhcp_context *context,
48 struct dhcp_packet *mess,
49 unsigned char *real_end,
50 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000051 char *hostname,
52 char *domain, char *config_domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000053 struct dhcp_netid *netid,
Simon Kelley7de060b2011-08-26 17:24:52 +010054 struct in_addr subnet_addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000055 unsigned char fqdn_flags,
Simon Kelley7622fc02009-06-04 20:32:05 +010056 int null_term, int pxearch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +010057 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +010058 int vendor_class_len,
59 time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010060
Simon Kelley9009d742008-11-14 20:04:27 +000061
Simon Kelley6b010842007-02-12 20:32:07 +000062static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Simon Kelley8ef5ada2010-06-03 19:42:45 +010063static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
Simon Kelley7622fc02009-06-04 20:32:05 +010064static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
65static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley316e2732010-01-22 20:16:09 +000066static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local);
Simon Kelley7622fc02009-06-04 20:32:05 +010067struct dhcp_boot *find_boot(struct dhcp_netid *netid);
68
69
Simon Kelley824af852008-02-12 20:43:05 +000070size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley7de060b2011-08-26 17:24:52 +010071 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
Simon Kelley33820b72004-04-03 21:10:00 +010072{
Simon Kelley26128d22004-11-14 16:43:54 +000073 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000074 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010075 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000076 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000077 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +010078 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000079 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000080 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010081 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000082 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000083 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010084 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000085 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000086 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000087 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010088 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010089 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010090 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010091 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010092 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010093 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000094 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010095 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000096 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000097 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010098 unsigned char pxe_uuid[17];
Simon Kelley316e2732010-01-22 20:16:09 +000099 unsigned char *oui = NULL, *serial = NULL, *class = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +0100100
Simon Kelley1a6bca82008-07-11 11:11:42 +0100101 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000102
103 /* set tag with name == interface */
104 iface_id.net = iface_name;
105 iface_id.next = NULL;
106 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100107
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100108 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100109 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000110
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000112 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100113
Simon Kelley3be34542004-09-11 19:12:13 +0100114 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000115 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000116 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100117 u32 cookie = htonl(DHCP_COOKIE);
118
Simon Kelley3be34542004-09-11 19:12:13 +0100119 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100120 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100121 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100122
123 mess_type = option_uint(opt, 0, 1);
124
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100125 /* two things to note here: expand_buf may move the packet,
126 so reassign mess from daemon->packet. Also, the size
127 sent includes the IP and UDP headers, hence the magic "-28" */
128 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
129 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100130 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100131
132 if (size > DHCP_PACKET_MAX)
133 size = DHCP_PACKET_MAX;
134 else if (size < sizeof(struct dhcp_packet))
135 size = sizeof(struct dhcp_packet);
136
137 if (expand_buf(&daemon->dhcp_packet, size))
138 {
Simon Kelley824af852008-02-12 20:43:05 +0000139 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100140 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100141 }
142 }
143
Simon Kelley3be34542004-09-11 19:12:13 +0100144 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
145 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000146 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100147 mess->ciaddr.s_addr = 0;
148
Simon Kelley316e2732010-01-22 20:16:09 +0000149 /* search for device identity from CPEWAN devices, we pass this through to the script */
150 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
151 {
152 unsigned int elen, offset, len = option_len(opt);
153
154 for (offset = 0; offset < (len - 5); offset += elen + 5)
155 {
156 elen = option_uint(opt, offset + 4 , 1);
157 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
158 {
159 unsigned char *x = option_ptr(opt, offset + 5);
160 unsigned char *y = option_ptr(opt, offset + elen + 5);
161 oui = option_find1(x, y, 1, 1);
162 serial = option_find1(x, y, 2, 1);
163 class = option_find1(x, y, 3, 1);
164
165 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
166 the gateway id back. Note that the device class is optional */
167 if (oui && serial)
168 {
169 cpewan_id.net = "cpewan-id";
170 cpewan_id.next = netid;
171 netid = &cpewan_id;
172 }
173 break;
174 }
175 }
176 }
177
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100178 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
179 {
180 /* Any agent-id needs to be copied back out, verbatim, as the last option
181 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
182 get overwritten, then it will be shuffled back at the end of processing.
183 Note that the incoming options must not be overwritten here, so there has to
184 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100185 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100186 unsigned int total = option_len(opt) + 2;
187 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
188 if (last_opt && last_opt < end - total)
189 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100190 end -= total;
191 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100192 memcpy(agent_id, opt, total);
193 }
194
195 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100196 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100197 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100198
199 /* look for RFC5107 server-identifier-override */
200 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
201 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100202
203 /* if a circuit-id or remote-is option is provided, exact-match to options. */
204 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
205 {
206 int search;
207
208 if (vendor->match_type == MATCH_CIRCUIT)
209 search = SUBOPT_CIRCUIT_ID;
210 else if (vendor->match_type == MATCH_REMOTE)
211 search = SUBOPT_REMOTE_ID;
212 else if (vendor->match_type == MATCH_SUBSCRIBER)
213 search = SUBOPT_SUBSCR_ID;
214 else
215 continue;
216
Simon Kelley1a6bca82008-07-11 11:11:42 +0100217 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100218 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100219 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100220 {
221 vendor->netid.next = netid;
222 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100223 }
224 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100225 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100226
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100227 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
228 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100229 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000230
231 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000232 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000233 {
Simon Kelley26128d22004-11-14 16:43:54 +0000234 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100235 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000236 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000237
Simon Kelley0a852542005-03-23 20:28:59 +0000238 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100239 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000240
241 /* If this request is missing a clid, but we've seen one before,
242 use it again for option matching etc. */
243 if (lease && !clid && lease->clid)
244 {
245 clid_len = lease->clid_len;
246 clid = lease->clid;
247 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000248
249 /* find mac to use for logging and hashing */
250 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000251 }
Simon Kelley3be34542004-09-11 19:12:13 +0100252
Simon Kelleycdeda282006-03-16 20:16:06 +0000253 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
254 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100255 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
256 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000257 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100258 mac->netid.next = netid;
259 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000260 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100261
Simon Kelley0a852542005-03-23 20:28:59 +0000262 /* Determine network for this packet. Our caller will have already linked all the
263 contexts which match the addresses of the receiving interface but if the
264 machine has an address already, or came via a relay, or we have a subnet selector,
265 we search again. If we don't have have a giaddr or explicit subnet selector,
266 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100267 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
268 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000269 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
270 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100271 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100272 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100273 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000274
Simon Kelley3d8df262005-08-29 12:19:27 +0100275 if (subnet_addr.s_addr)
276 {
277 addr = subnet_addr;
278 force = 1;
279 }
280 else if (mess->giaddr.s_addr)
281 {
282 addr = mess->giaddr;
283 force = 1;
284 }
Simon Kelley16972692006-10-16 20:04:18 +0100285 else
286 {
287 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
288 addr = mess->ciaddr;
289 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
290 if (context_tmp->netmask.s_addr &&
291 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
292 is_same_net(addr, context_tmp->end, context_tmp->netmask))
293 {
294 context_new = context;
295 break;
296 }
297 }
298
299 if (!context_new)
300 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100301 {
302 struct in_addr netmask = context_tmp->netmask;
303
304 /* guess the netmask for relayed networks */
305 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
306 {
307 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
308 netmask.s_addr = htonl(0xff000000);
309 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
310 netmask.s_addr = htonl(0xffff0000);
311 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
312 netmask.s_addr = htonl(0xffffff00);
313 }
314
315 /* This section fills in context mainly when a client which is on a remote (relayed)
316 network renews a lease without using the relay, after dnsmasq has restarted. */
317 if (netmask.s_addr != 0 &&
318 is_same_net(addr, context_tmp->start, netmask) &&
319 is_same_net(addr, context_tmp->end, netmask))
320 {
321 context_tmp->netmask = netmask;
322 if (context_tmp->local.s_addr == 0)
323 context_tmp->local = fallback;
324 if (context_tmp->router.s_addr == 0)
325 context_tmp->router = mess->giaddr;
326
327 /* fill in missing broadcast addresses for relayed ranges */
328 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
329 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
330
331 context_tmp->current = context_new;
332 context_new = context_tmp;
333 }
334 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100335
Simon Kelley3d8df262005-08-29 12:19:27 +0100336 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100337 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000338 }
Simon Kelley3be34542004-09-11 19:12:13 +0100339
340 if (!context)
341 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100342 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100343 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
344 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 +0100345 return 0;
346 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100347
Simon Kelley28866e92011-02-14 20:19:14 +0000348 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100349 {
350 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100351 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
352 {
353 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100354 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100355 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100356 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100357 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100358 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100359 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100360 }
361 }
362
Simon Kelley3be34542004-09-11 19:12:13 +0100363 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100364
Simon Kelleycdeda282006-03-16 20:16:06 +0000365 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
366 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100367
368 /* set "known" tag for known hosts */
369 if (config)
370 {
371 known_id.net = "known";
372 known_id.next = netid;
373 netid = &known_id;
374 }
Simon Kelley26128d22004-11-14 16:43:54 +0000375
Simon Kelley316e2732010-01-22 20:16:09 +0000376 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100377 {
378 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000379 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000380 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100381
382 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100383 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100384 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000385
Simon Kelley26128d22004-11-14 16:43:54 +0000386 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000387 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000388
389 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
390
391 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000392 {
393 hostname = config->hostname;
394 domain = config->domain;
395 }
396
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100397 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000398 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100399 struct dhcp_netid_list *list;
400
401 for (list = config->netid; list; list = list->next)
402 {
403 list->list->next = netid;
404 netid = list->list;
405 }
Simon Kelley26128d22004-11-14 16:43:54 +0000406 }
407
408 /* Match incoming filename field as a netid. */
409 if (mess->file[0])
410 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000411 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
412 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
413 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000414 id.next = netid;
415 netid = &id;
416 }
Simon Kelley6b010842007-02-12 20:32:07 +0000417
418 /* Add "bootp" as a tag to allow different options, address ranges etc
419 for BOOTP clients */
420 bootp_id.net = "bootp";
421 bootp_id.next = netid;
422 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000423
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 tagif_netid = run_tag_if(netid);
425
Simon Kelley26128d22004-11-14 16:43:54 +0000426 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100427 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100428 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000429
Simon Kelley3d8df262005-08-29 12:19:27 +0100430 if (!message)
431 {
Simon Kelley9009d742008-11-14 20:04:27 +0000432 int nailed = 0;
433
Simon Kelley3d8df262005-08-29 12:19:27 +0100434 if (have_config(config, CONFIG_ADDR))
435 {
Simon Kelley9009d742008-11-14 20:04:27 +0000436 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100437 logaddr = &config->addr;
438 mess->yiaddr = config->addr;
439 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000440 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100441 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000442 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000443 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100444 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100445 else
446 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000447 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100448 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000449 {
450 if (lease)
451 {
452 /* lease exists, wrong network. */
453 lease_prune(lease, now);
454 lease = NULL;
455 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100456 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000457 message = _("no address available");
458 }
459 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100460 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100461 }
462
Simon Kelley9009d742008-11-14 20:04:27 +0000463 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
464 message = _("wrong network");
465 else if (context->netid.net)
466 {
467 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100468 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100469 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100470
471 log_tags(tagif_netid, mess);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100472
Simon Kelley9009d742008-11-14 20:04:27 +0000473 if (!message && !nailed)
474 {
475 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100476 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000477 break;
478 if (!id_list)
479 message = _("no address configured");
480 }
481
Simon Kelley7cebd202006-05-06 14:13:33 +0100482 if (!message &&
483 !lease &&
484 (!(lease = lease_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000485 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000486
Simon Kelley3d8df262005-08-29 12:19:27 +0100487 if (!message)
488 {
489 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100490
Simon Kelleycdeda282006-03-16 20:16:06 +0000491 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100492 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +0000493 lease_set_hostname(lease, hostname, 1);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100494 /* infinite lease unless nailed in dhcp-host line. */
495 lease_set_expires(lease,
496 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
497 now);
Simon Kelley824af852008-02-12 20:43:05 +0000498 lease_set_interface(lease, int_index);
Simon Kelley3d8df262005-08-29 12:19:27 +0100499
Simon Kelley7622fc02009-06-04 20:32:05 +0100500 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000501 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +0100502 domain, netid, subnet_addr, 0, 0, 0, NULL, 0, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100503 }
504 }
505
Simon Kelley7622fc02009-06-04 20:32:05 +0100506 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000507
Simon Kelley7de060b2011-08-26 17:24:52 +0100508 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100509 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000510
Simon Kelley3d8df262005-08-29 12:19:27 +0100511 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
512 {
513 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
514 int len = option_len(opt);
515 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100516 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100517
518 fqdn_flags = *op;
519 len -= 3;
520 op += 3;
521 pp = op;
522
523 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000524 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100525 fqdn_flags |= 0x02;
526
527 fqdn_flags &= ~0x08;
528 fqdn_flags |= 0x01;
529
530 if (fqdn_flags & 0x04)
531 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
532 {
533 memcpy(pq, op+1, *op);
534 pq += *op;
535 op += (*op)+1;
536 *(pq++) = '.';
537 }
538 else
539 {
540 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000541 if (len > 0 && op[len-1] == 0)
542 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100543 pq += len + 1;
544 }
545
546 if (pq != daemon->dhcp_buff)
547 pq--;
548
549 *pq = 0;
550
Simon Kelley1f15b812009-10-13 17:49:32 +0100551 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100552 offer_hostname = client_hostname = daemon->dhcp_buff;
553 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000554 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000555 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000556 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100557 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000558 /* Microsoft clients are broken, and need zero-terminated strings
559 in options. We detect this state here, and do the same in
560 any options we send */
561 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
562 borken_opt = 1;
563 else
564 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100565 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100566 client_hostname = daemon->dhcp_buff;
567 }
568
Simon Kelley28866e92011-02-14 20:19:14 +0000569 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100570 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
571
Simon Kelley3d8df262005-08-29 12:19:27 +0100572 if (have_config(config, CONFIG_NAME))
573 {
574 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000575 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000576 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000577 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100578 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000579 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100580 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100581 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100582 {
Simon Kelley9009d742008-11-14 20:04:27 +0000583 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100584
585 if (strlen(client_hostname) != 0)
586 {
587 hostname = client_hostname;
588 if (!config)
589 {
590 /* Search again now we have a hostname.
591 Only accept configs without CLID and HWADDR here, (they won't match)
592 to avoid impersonation by name. */
593 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
594 mess->chaddr, mess->hlen,
595 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000596 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000597 {
598 config = new;
599 /* set "known" tag for known hosts */
600 known_id.net = "known";
601 known_id.next = netid;
602 netid = &known_id;
603 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100604 }
605 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000606 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100607
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100608 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100609 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100610 struct dhcp_netid_list *list;
611
612 for (list = config->netid; list; list = list->next)
613 {
614 list->list->next = netid;
615 netid = list->list;
616 }
Simon Kelleya2226412004-05-13 20:27:08 +0100617 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100618
Simon Kelley73a08a22009-02-05 20:28:08 +0000619 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
620 Otherwise assume the option is an array, and look for a matching element.
Simon Kelley316e2732010-01-22 20:16:09 +0000621 If no data given, existance of the option is enough. This code handles
622 rfc3925 V-I classes too. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000623 for (o = daemon->dhcp_match; o; o = o->next)
624 {
Simon Kelley316e2732010-01-22 20:16:09 +0000625 unsigned int len, elen, match = 0;
626 size_t offset, o2;
Simon Kelley73a08a22009-02-05 20:28:08 +0000627
Simon Kelley316e2732010-01-22 20:16:09 +0000628 if (o->flags & DHOPT_RFC3925)
629 {
630 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
631 continue;
632
633 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
634 {
635 len = option_uint(opt, offset + 4 , 1);
636 /* Need to take care that bad data can't run us off the end of the packet */
637 if ((offset + len + 5 <= (option_len(opt))) &&
638 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
639 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
640 {
641 elen = option_uint(opt, o2, 1);
642 if ((o2 + elen + 1 <= option_len(opt)) &&
643 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
644 break;
645 }
646 if (match)
647 break;
648 }
649 }
650 else
651 {
652 if (!(opt = option_find(mess, sz, o->opt, 1)))
653 continue;
654
655 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
656 }
657
658 if (match)
Simon Kelley73a08a22009-02-05 20:28:08 +0000659 {
660 o->netid->next = netid;
661 netid = o->netid;
662 }
663 }
664
Simon Kelley26128d22004-11-14 16:43:54 +0000665 /* user-class options are, according to RFC3004, supposed to contain
666 a set of counted strings. Here we check that this is so (by seeing
667 if the counts are consistent with the overall option length) and if
668 so zero the counts so that we don't get spurious matches between
669 the vendor string and the counts. If the lengths don't add up, we
670 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100671 and just do the substring match. dhclient provides these broken options.
672 The code, later, which sends user-class data to the lease-change script
673 relies on the transformation done here.
674 */
Simon Kelleya2226412004-05-13 20:27:08 +0100675
Simon Kelleybb01cb92004-12-13 20:56:23 +0000676 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100677 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100678 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000679 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000680 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
681 if (j == option_len(opt))
682 for (j = 0; j < option_len(opt); j = tmp)
683 {
684 tmp = j + ucp[j] + 1;
685 ucp[j] = 0;
686 }
Simon Kelleya2226412004-05-13 20:27:08 +0100687 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000688
Simon Kelley26128d22004-11-14 16:43:54 +0000689 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100690 {
691 int mopt;
692
693 if (vendor->match_type == MATCH_VENDOR)
694 mopt = OPTION_VENDOR_ID;
695 else if (vendor->match_type == MATCH_USER)
696 mopt = OPTION_USER_CLASS;
697 else
698 continue;
699
700 if ((opt = option_find(mess, sz, mopt, 1)))
701 {
702 int i;
703 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100704 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100705 {
706 vendor->netid.next = netid;
707 netid = &vendor->netid;
708 break;
709 }
710 }
711 }
712
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100713 /* mark vendor-encapsulated options which match the client-supplied vendor class,
714 save client-supplied vendor class */
715 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
716 {
717 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
718 vendor_class_len = option_len(opt);
719 }
720 match_vendor_opts(opt, daemon->dhcp_opts);
721
Simon Kelley28866e92011-02-14 20:19:14 +0000722 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100723 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100724 if (sanitise(opt, daemon->namebuff))
725 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100726 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100727 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100728 }
729
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100730 tagif_netid = run_tag_if(netid);
731
Simon Kelley26128d22004-11-14 16:43:54 +0000732 /* if all the netids in the ignore list are present, ignore this client */
733 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100734 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000735 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100736
737 /* If configured, we can override the server-id to be the address of the relay,
738 so that all traffic goes via the relay and can pick up agent-id info. This can be
739 configured for all relays, or by address. */
740 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
741 {
742 if (!daemon->override_relays)
743 override = mess->giaddr;
744 else
745 {
746 struct addr_list *l;
747 for (l = daemon->override_relays; l; l = l->next)
748 if (l->addr.s_addr == mess->giaddr.s_addr)
749 break;
750 if (l)
751 override = mess->giaddr;
752 }
753 }
754
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100755 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
756 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000757 clid = NULL;
758
Simon Kelley7622fc02009-06-04 20:32:05 +0100759 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100760 if (daemon->enable_pxe &&
761 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100762 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
763 {
764 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
765 {
766 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
767 uuid = pxe_uuid;
768 }
769
770 /* Check if this is really a PXE bootserver request, and handle specially if so. */
771 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
772 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
773 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
774 {
775 struct pxe_service *service;
776 int type = option_uint(opt, 0, 2);
777 int layer = option_uint(opt, 2, 2);
778 unsigned char save71[4];
779 struct dhcp_opt opt71;
780
Simon Kelley1f15b812009-10-13 17:49:32 +0100781 if (ignore)
782 return 0;
783
Simon Kelley7622fc02009-06-04 20:32:05 +0100784 if (layer & 0x8000)
785 {
786 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
787 return 0;
788 }
789
790 memcpy(save71, option_ptr(opt, 0), 4);
791
792 for (service = daemon->pxe_services; service; service = service->next)
793 if (service->type == type)
794 break;
795
796 if (!service || !service->basename)
797 return 0;
798
799 clear_packet(mess, end);
800
801 mess->yiaddr = mess->ciaddr;
802 mess->ciaddr.s_addr = 0;
803 if (service->server.s_addr != 0)
804 mess->siaddr = service->server;
805 else
806 mess->siaddr = context->local;
807
808 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
809 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
810 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
811 pxe_misc(mess, end, uuid);
812
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100813 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100814 opt71.val = save71;
815 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
816 opt71.len = 4;
817 opt71.flags = DHOPT_VENDOR_MATCH;
818 opt71.netid = NULL;
819 opt71.next = daemon->dhcp_opts;
820 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
821
822 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100823 log_tags(tagif_netid, mess);
824 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100825 }
826
827 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
828 {
829 pxearch = option_uint(opt, 0, 2);
830
Simon Kelley316e2732010-01-22 20:16:09 +0000831 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000832 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 {
Simon Kelley28866e92011-02-14 20:19:14 +0000834 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100835
Simon Kelley28866e92011-02-14 20:19:14 +0000836 for (tmp = context; tmp; tmp = tmp->current)
837 if ((tmp->flags & CONTEXT_PROXY) &&
838 match_netid(tmp->filter, tagif_netid, 1))
839 break;
840
841 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100842 {
Simon Kelley28866e92011-02-14 20:19:14 +0000843 struct dhcp_boot *boot = find_boot(tagif_netid);
844
845 mess->yiaddr.s_addr = 0;
846 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
847 {
848 mess->ciaddr.s_addr = 0;
849 mess->flags |= htons(0x8000); /* broadcast */
850 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100851
Simon Kelley28866e92011-02-14 20:19:14 +0000852 clear_packet(mess, end);
853
854 /* Provide the bootfile here, for gPXE, and in case we have no menu items
855 and set discovery_control = 8 */
856 if (boot)
857 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100858 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000859 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100860 else if (boot->tftp_sname)
861 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000862
863 if (boot->file)
864 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
865 }
866
867 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
868 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
869 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
870 pxe_misc(mess, end, uuid);
871 prune_vendor_opts(tagif_netid);
872 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
873
874 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100875 log_tags(tagif_netid, mess);
876 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100877 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100878 }
879 }
880 }
881
882 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000883 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100884 return 0;
885
Simon Kelleybb01cb92004-12-13 20:56:23 +0000886 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100887 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100888 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100889 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000890 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100891 }
892
Simon Kelley3be34542004-09-11 19:12:13 +0100893 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000894 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000895 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000896 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000897 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000898 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100899
Simon Kelley44a2a312004-03-10 20:04:35 +0000900 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100901 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000902
Simon Kelleybb01cb92004-12-13 20:56:23 +0000903 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000904 return 0;
905
Simon Kelley7622fc02009-06-04 20:32:05 +0100906 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000907
908 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
909 lease_prune(lease, now);
910
Simon Kelley33820b72004-04-03 21:10:00 +0100911 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000912 config->addr.s_addr == option_addr(opt).s_addr)
913 {
Simon Kelley849a8352006-06-09 21:02:31 +0100914 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100915 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100916 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100917 config->flags |= CONFIG_DECLINED;
918 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000919 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100920 else
921 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100922 for (; context; context = context->current)
923 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000924
925 return 0;
926
927 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100928 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100929 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000930 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000931 return 0;
932
Simon Kelley44a2a312004-03-10 20:04:35 +0000933 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
934 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100935 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000936 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100937
Simon Kelley7622fc02009-06-04 20:32:05 +0100938 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000939
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000940 return 0;
941
942 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100943 if (ignore || have_config(config, CONFIG_DISABLE))
944 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000945 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100946 opt = NULL;
947 }
948 else
949 {
950 struct in_addr addr, conf;
951
Simon Kelley1a6bca82008-07-11 11:11:42 +0100952 addr.s_addr = conf.s_addr = 0;
953
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100954 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
955 addr = option_addr(opt);
956
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100957 if (have_config(config, CONFIG_ADDR))
958 {
Simon Kelley849a8352006-06-09 21:02:31 +0100959 char *addrs = inet_ntoa(config->addr);
960
Simon Kelley9009d742008-11-14 20:04:27 +0000961 if ((ltmp = lease_find_by_addr(config->addr)) &&
962 ltmp != lease &&
963 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000964 {
965 int len;
966 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
967 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100968 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100969 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000970 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100971 else
972 {
973 struct dhcp_context *tmp;
974 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100975 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100976 break;
977 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100978 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
Simon Kelley849a8352006-06-09 21:02:31 +0100979 else if (have_config(config, CONFIG_DECLINED) &&
980 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100981 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100982 else
983 conf = config->addr;
984 }
985 }
986
987 if (conf.s_addr)
988 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100989 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100990 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100991 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100992 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100993 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100994 !config_find_by_address(daemon->dhcp_conf, addr))
995 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100996 else if (emac_len == 0)
997 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100998 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100999 message = _("no address available");
1000 }
1001
Simon Kelley7622fc02009-06-04 20:32:05 +01001002 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001003
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001004 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001005 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001006
Simon Kelleycdeda282006-03-16 20:16:06 +00001007 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001008 {
1009 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001010 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001011 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001012
1013 log_tags(tagif_netid, mess);
1014
1015 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1016
Simon Kelley824af852008-02-12 20:43:05 +00001017 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001018 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001019 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001020 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001021 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001022 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001023 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001024 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001025 option_put(mess, end, OPTION_T1, 4, (time/2));
1026 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001027 }
Simon Kelley9009d742008-11-14 20:04:27 +00001028 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001029 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001030
Simon Kelley7de060b2011-08-26 17:24:52 +01001031 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001032
1033 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001034 if (ignore || have_config(config, CONFIG_DISABLE))
1035 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001036 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001037 {
1038 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001039 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001040
Simon Kelley4011c4e2006-10-28 16:26:19 +01001041 /* send vendor and user class info for new or recreated lease */
1042 do_classes = 1;
1043
Simon Kelleybb01cb92004-12-13 20:56:23 +00001044 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001045 {
Simon Kelley3be34542004-09-11 19:12:13 +01001046 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001047 selecting = 1;
1048
Simon Kelley1a6bca82008-07-11 11:11:42 +01001049 if (override.s_addr != 0)
1050 {
1051 if (option_addr(opt).s_addr != override.s_addr)
1052 return 0;
1053 }
Simon Kelley9009d742008-11-14 20:04:27 +00001054 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001055 {
1056 for (; context; context = context->current)
1057 if (context->local.s_addr == option_addr(opt).s_addr)
1058 break;
1059
1060 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001061 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001062 /* Handle very strange configs where clients have more than one route to the server.
1063 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1064 Have to set override to make sure we echo back the correct server-id */
1065 struct irec *intr;
1066
1067 enumerate_interfaces();
1068
1069 for (intr = daemon->interfaces; intr; intr = intr->next)
1070 if (intr->addr.sa.sa_family == AF_INET &&
1071 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1072 intr->tftp_ok)
1073 break;
1074
1075 if (intr)
1076 override = intr->addr.in.sin_addr;
1077 else
1078 {
1079 /* In auth mode, a REQUEST sent to the wrong server
1080 should be faulted, so that the client establishes
1081 communication with us, otherwise, silently ignore. */
1082 if (!option_bool(OPT_AUTHORITATIVE))
1083 return 0;
1084 message = _("wrong server-ID");
1085 }
Simon Kelley9009d742008-11-14 20:04:27 +00001086 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001087 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001088
Simon Kelley3be34542004-09-11 19:12:13 +01001089 /* If a lease exists for this host and another address, squash it. */
1090 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1091 {
1092 lease_prune(lease, now);
1093 lease = NULL;
1094 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001095 }
Simon Kelley3be34542004-09-11 19:12:13 +01001096 else
1097 {
1098 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001099 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001100 return 0;
1101
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001102 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001103 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001104 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001105 }
1106 else
1107 {
1108 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001109 /* Check existing lease for this address.
1110 We allow it to be missing if dhcp-authoritative mode
1111 as long as we can allocate the lease now - checked below.
1112 This makes for a smooth recovery from a lost lease DB */
1113 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001114 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001115 {
Simon Kelley28866e92011-02-14 20:19:14 +00001116 /* A client rebinding will broadcast the request, so we may see it even
1117 if the lease is held by another server. Just ignore it in that case.
1118 If the request is unicast to us, then somethings wrong, NAK */
1119 if (!unicast_dest)
1120 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001121 message = _("lease not found");
1122 /* ensure we broadcast NAK */
1123 unicast_dest = 0;
1124 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001125
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001126 /* desynchronise renewals */
1127 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001128 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001129 }
1130
Simon Kelley7622fc02009-06-04 20:32:05 +01001131 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001132
Simon Kelleydfa666f2004-08-02 18:27:27 +01001133 if (!message)
1134 {
1135 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001136 struct dhcp_context *tmp = NULL;
1137
1138 if (have_config(config, CONFIG_ADDR))
1139 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001140 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001141 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001142
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001143 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001144 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001145 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001146 message = _("wrong network");
1147 /* ensure we broadcast NAK */
1148 unicast_dest = 0;
1149 }
1150
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001151 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001152 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001153 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001154 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001155
Simon Kelleydfa666f2004-08-02 18:27:27 +01001156 /* Check if a new static address has been configured. Be very sure that
1157 when the client does DISCOVER, it will get the static address, otherwise
1158 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001159 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001160 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001161 (!have_config(config, CONFIG_DECLINED) ||
1162 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001163 config->addr.s_addr != mess->yiaddr.s_addr &&
1164 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001165 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001166
1167 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001168 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001169 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001170
Simon Kelley9009d742008-11-14 20:04:27 +00001171 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1172 {
1173 /* If a host is configured with more than one MAC address, it's OK to 'nix
1174 a lease from one of it's MACs to give the address to another. */
1175 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1176 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001177 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001178 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1179 inet_ntoa(ltmp->addr));
1180 lease = ltmp;
1181 }
Simon Kelley16972692006-10-16 20:04:18 +01001182 else
Simon Kelley9009d742008-11-14 20:04:27 +00001183 message = _("address in use");
1184 }
1185
1186 if (!message)
1187 {
1188 if (emac_len == 0)
1189 message = _("no unique-id");
1190
1191 else if (!lease)
1192 {
1193 if ((lease = lease_allocate(mess->yiaddr)))
1194 do_classes = 1;
1195 else
1196 message = _("no leases left");
1197 }
Simon Kelley16972692006-10-16 20:04:18 +01001198 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001199 }
Simon Kelley16972692006-10-16 20:04:18 +01001200
Simon Kelley44a2a312004-03-10 20:04:35 +00001201 if (message)
1202 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001203 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001204
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001205 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001206 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001207 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001208 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001209 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001210 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1211 a distant subnet which unicast a REQ to us won't work. */
1212 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1213 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1214 {
1215 mess->flags |= htons(0x8000); /* broadcast */
1216 mess->ciaddr.s_addr = 0;
1217 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001218 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001219 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001220 {
Simon Kelley316e2732010-01-22 20:16:09 +00001221 if (context->netid.net)
1222 {
1223 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001224 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001225 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001226
1227 log_tags(tagif_netid, mess);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001228
Simon Kelley316e2732010-01-22 20:16:09 +00001229#ifdef HAVE_SCRIPT
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001230 if (do_classes && daemon->lease_change_command)
1231 {
1232 struct dhcp_netid *n;
1233
1234 if (mess->giaddr.s_addr)
1235 lease->giaddr = mess->giaddr;
1236
1237 lease->changed = 1;
1238 free(lease->extradata);
Simon Kelley28866e92011-02-14 20:19:14 +00001239 lease->extradata = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001240 lease->extradata_size = lease->extradata_len = 0;
1241
1242 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1243 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1244 add_extradata_opt(lease, oui);
1245 add_extradata_opt(lease, serial);
1246 add_extradata_opt(lease, class);
1247
1248 /* space-concat tag set */
1249 if (!tagif_netid)
1250 add_extradata_opt(lease, NULL);
1251 else
1252 for (n = tagif_netid; n; n = n->next)
1253 add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1254
1255 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1256 {
1257 int len = option_len(opt);
1258 unsigned char *ucp = option_ptr(opt, 0);
1259 /* If the user-class option started as counted strings, the first byte will be zero. */
1260 if (len != 0 && ucp[0] == 0)
1261 ucp++, len--;
1262 add_extradata_data(lease, ucp, len, 0);
1263 }
1264 }
Simon Kelley316e2732010-01-22 20:16:09 +00001265#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001266
1267 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1268 {
1269 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001270 hostname = client_hostname;
1271 hostname_auth = 1;
1272 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001273
Simon Kelley824af852008-02-12 20:43:05 +00001274 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +00001275 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001276
Simon Kelley832af0b2007-01-21 20:01:28 +00001277 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1278 if (!hostname_auth)
1279 {
1280 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001281 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001282 break;
1283 if (id_list)
1284 hostname = NULL;
1285 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001286
1287 /* Last ditch, if configured, generate hostname from mac address */
1288 if (!hostname && emac_len != 0)
1289 {
1290 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1291 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1292 break;
1293 if (id_list)
1294 {
1295 int i;
1296
1297 hostname = daemon->dhcp_buff;
1298 /* buffer is 256 bytes, 3 bytes per octet */
1299 for (i = 0; (i < emac_len) && (i < 80); i++)
1300 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1301 hostname = daemon->dhcp_buff;
1302 }
1303 }
1304
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001305 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +00001306 lease_set_hostname(lease, hostname, hostname_auth);
Simon Kelley832af0b2007-01-21 20:01:28 +00001307
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001308 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +00001309 lease_set_interface(lease, int_index);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001310
1311 if (override.s_addr != 0)
1312 lease->override = override;
1313 else
1314 override = lease->override;
1315
Simon Kelley7622fc02009-06-04 20:32:05 +01001316 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001317
Simon Kelley7622fc02009-06-04 20:32:05 +01001318 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001319 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001320 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001321 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001322 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001323 {
Simon Kelley59353a62004-11-21 19:34:28 +00001324 while (fuzz > (time/16))
1325 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001326 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1327 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001328 }
Simon Kelley9009d742008-11-14 20:04:27 +00001329 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001330 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001331 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001332
Simon Kelley7de060b2011-08-26 17:24:52 +01001333 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001334
1335 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001336 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001337 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001338
Simon Kelley7622fc02009-06-04 20:32:05 +01001339 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001340
Simon Kelley73a08a22009-02-05 20:28:08 +00001341 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001342 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001343
1344 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001345 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001346
Simon Kelley5aabfc72007-08-29 11:24:47 +01001347 /* Find a least based on IP address if we didn't
1348 get one from MAC address/client-d */
1349 if (!lease &&
1350 (lease = lease_find_by_addr(mess->ciaddr)) &&
1351 lease->hostname)
1352 hostname = lease->hostname;
1353
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001354 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1355 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001356
Simon Kelley73a08a22009-02-05 20:28:08 +00001357 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001358 {
1359 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001360 tagif_netid = run_tag_if( &context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001361 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001362
1363 log_tags(tagif_netid, mess);
1364
1365 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001366
Simon Kelley3927da42008-07-20 15:10:39 +01001367 if (lease)
1368 {
1369 if (override.s_addr != 0)
1370 lease->override = override;
1371 else
1372 override = lease->override;
1373 }
1374
Simon Kelley7622fc02009-06-04 20:32:05 +01001375 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001376 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001377 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1378
Simon Kelley5aabfc72007-08-29 11:24:47 +01001379 if (lease)
1380 {
1381 if (lease->expires == 0)
1382 time = 0xffffffff;
1383 else
1384 time = (unsigned int)difftime(lease->expires, now);
1385 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001386 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001387 }
Simon Kelley824af852008-02-12 20:43:05 +00001388
Simon Kelley9009d742008-11-14 20:04:27 +00001389 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001390 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001391
Simon Kelley5aabfc72007-08-29 11:24:47 +01001392 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001393 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001394 }
1395
1396 return 0;
1397}
1398
Simon Kelley316e2732010-01-22 20:16:09 +00001399static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
1400{
1401 int i;
1402
1403 if (o->len > len)
1404 return 0;
1405
1406 if (o->len == 0)
1407 return 1;
1408
1409 if (o->flags & DHOPT_HEX)
1410 {
1411 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
1412 return 1;
1413 }
1414 else
1415 for (i = 0; i <= (len - o->len); )
1416 {
1417 if (memcmp(o->val, p + i, o->len) == 0)
1418 return 1;
1419
1420 if (o->flags & DHOPT_STRING)
1421 i++;
1422 else
1423 i += o->len;
1424 }
1425
1426 return 0;
1427}
1428
1429
Simon Kelley6b010842007-02-12 20:32:07 +00001430/* find a good value to use as MAC address for logging and address-allocation hashing.
1431 This is normally just the chaddr field from the DHCP packet,
1432 but eg Firewire will have hlen == 0 and use the client-id instead.
1433 This could be anything, but will normally be EUI64 for Firewire.
1434 We assume that if the first byte of the client-id equals the htype byte
1435 then the client-id is using the usual encoding and use the rest of the
1436 client-id: if not we can use the whole client-id. This should give
1437 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001438unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001439 int clid_len, unsigned char *clid, int *len_out)
1440{
1441 if (hwlen == 0 && clid && clid_len > 3)
1442 {
1443 if (clid[0] == hwtype)
1444 {
1445 *len_out = clid_len - 1 ;
1446 return clid + 1;
1447 }
1448
1449#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1450 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1451 {
1452 *len_out = clid_len - 1 ;
1453 return clid + 1;
1454 }
1455#endif
1456
1457 *len_out = clid_len;
1458 return clid;
1459 }
1460
1461 *len_out = hwlen;
1462 return hwaddr;
1463}
1464
Simon Kelley824af852008-02-12 20:43:05 +00001465static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001466{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001467 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001468
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001469 if (opt)
1470 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001471 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001472 if (req_time < 120 )
1473 req_time = 120; /* sanity */
1474 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1475 time = req_time;
1476 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001477
1478 return time;
1479}
1480
Simon Kelley73a08a22009-02-05 20:28:08 +00001481static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001482{
Simon Kelley73a08a22009-02-05 20:28:08 +00001483 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001484 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001485 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001486 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001487 else
1488 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001489}
1490
Simon Kelleyf2621c72007-04-29 19:47:21 +01001491static int sanitise(unsigned char *opt, char *buf)
1492{
1493 char *p;
1494 int i;
1495
1496 *buf = 0;
1497
1498 if (!opt)
1499 return 0;
1500
Simon Kelley1a6bca82008-07-11 11:11:42 +01001501 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001502
1503 for (i = option_len(opt); i > 0; i--)
1504 {
1505 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001506 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001507 *buf++ = c;
1508 }
1509 *buf = 0; /* add terminator */
1510
1511 return 1;
1512}
1513
Simon Kelley316e2732010-01-22 20:16:09 +00001514#ifdef HAVE_SCRIPT
1515static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
1516{
1517 if ((lease->extradata_size - lease->extradata_len) < (len + 1))
1518 {
1519 size_t newsz = lease->extradata_len + len + 100;
1520 unsigned char *new = whine_malloc(newsz);
1521
1522 if (!new)
1523 return;
1524
1525 if (lease->extradata)
1526 {
1527 memcpy(new, lease->extradata, lease->extradata_len);
1528 free(lease->extradata);
1529 }
1530
1531 lease->extradata = new;
1532 lease->extradata_size = newsz;
1533 }
1534
1535 if (len != 0)
1536 memcpy(lease->extradata + lease->extradata_len, data, len);
1537 lease->extradata[lease->extradata_len + len] = delim;
1538 lease->extradata_len += len + 1;
1539}
1540
1541static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1542{
1543 if (!opt)
1544 add_extradata_data(lease, NULL, 0, 0);
1545 else
1546 {
1547 size_t i, len = option_len(opt);
1548 unsigned char *ucp = option_ptr(opt, 0);
1549
1550 /* check for embeded NULLs */
1551 for (i = 0; i < len; i++)
1552 if (ucp[i] == 0)
1553 {
1554 len = i;
1555 break;
1556 }
1557
1558 add_extradata_data(lease, ucp, len, 0);
1559 }
1560}
1561#endif
1562
Simon Kelley5aabfc72007-08-29 11:24:47 +01001563static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001564 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001565{
Simon Kelley16972692006-10-16 20:04:18 +01001566 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001567
Simon Kelley16972692006-10-16 20:04:18 +01001568 /* addr may be misaligned */
1569 if (addr)
1570 memcpy(&a, addr, sizeof(a));
1571
Simon Kelley7622fc02009-06-04 20:32:05 +01001572 print_mac(daemon->namebuff, ext_mac, mac_len);
1573
Simon Kelley28866e92011-02-14 20:19:14 +00001574 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001575 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1576 ntohl(xid),
1577 type,
1578 interface,
1579 addr ? inet_ntoa(a) : "",
1580 addr ? " " : "",
1581 daemon->namebuff,
1582 string ? string : "");
1583 else
1584 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1585 type,
1586 interface,
1587 addr ? inet_ntoa(a) : "",
1588 addr ? " " : "",
1589 daemon->namebuff,
1590 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001591}
1592
Simon Kelley7622fc02009-06-04 20:32:05 +01001593static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001594{
1595 while (*start != OPTION_END)
1596 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001597 int is_ip, is_name, i;
1598 char *text = option_string(start[0], &is_ip, &is_name);
1599 unsigned char trunc = option_len(start);
1600
1601 if (is_ip)
1602 for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ)
1603 {
1604 if (i != 0)
1605 strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
1606 strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
1607 }
1608 else if (!is_name || !sanitise(start, daemon->namebuff))
1609 {
1610 if (trunc > 13)
1611 trunc = 13;
1612 print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
1613 }
1614
1615 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
1616 ntohl(xid), option_len(start), start[0],
Simon Kelleyf2621c72007-04-29 19:47:21 +01001617 text ? ":" : "", text ? text : "",
Simon Kelley7622fc02009-06-04 20:32:05 +01001618 trunc == 0 ? "" : " ",
1619 trunc == 0 ? "" : daemon->namebuff,
1620 trunc == option_len(start) ? "" : "...");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001621 start += start[1] + 2;
1622 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001623}
1624
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001625static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001626{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001627 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001628 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001629 if (p > end)
1630 return NULL;
1631 else if (*p == OPTION_END)
1632 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001633 else if (*p == OPTION_PAD)
1634 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001635 else
1636 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001637 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001638 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001639 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001640 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001641 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001642 return NULL; /* malformed packet */
1643 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001644 return p;
1645 p += opt_len + 2;
1646 }
1647 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001648}
1649
Simon Kelleycdeda282006-03-16 20:16:06 +00001650static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001651{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001652 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001653
Simon Kelley3be34542004-09-11 19:12:13 +01001654 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001655 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1656 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001657
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001658 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001659 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001660 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001661
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001662 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001663 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001664 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1665 return ret;
1666
1667 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001668 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001669 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1670 return ret;
1671
1672 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001673}
1674
Simon Kelley7622fc02009-06-04 20:32:05 +01001675static struct in_addr option_addr_arr(unsigned char *opt, int offset)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001676{
1677 /* this worries about unaligned data in the option. */
1678 /* struct in_addr is network byte order */
1679 struct in_addr ret;
1680
Simon Kelley7622fc02009-06-04 20:32:05 +01001681 memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001682
1683 return ret;
1684}
1685
Simon Kelley7622fc02009-06-04 20:32:05 +01001686static struct in_addr option_addr(unsigned char *opt)
1687{
1688 return option_addr_arr(opt, 0);
1689}
1690
1691static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001692{
1693 /* this worries about unaligned data and byte order */
1694 unsigned int ret = 0;
1695 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001696 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001697
1698 for (i = 0; i < size; i++)
1699 ret = (ret << 8) | *p++;
1700
1701 return ret;
1702}
1703
1704static unsigned char *dhcp_skip_opts(unsigned char *start)
1705{
1706 while (*start != 0)
1707 start += start[1] + 2;
1708 return start;
1709}
1710
1711/* only for use when building packet: doesn't check for bad data. */
1712static unsigned char *find_overload(struct dhcp_packet *mess)
1713{
1714 unsigned char *p = &mess->options[0] + sizeof(u32);
1715
1716 while (*p != 0)
1717 {
1718 if (*p == OPTION_OVERLOAD)
1719 return p;
1720 p += p[1] + 2;
1721 }
1722 return NULL;
1723}
1724
Simon Kelley7de060b2011-08-26 17:24:52 +01001725static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001726{
Simon Kelley28866e92011-02-14 20:19:14 +00001727 if (netid && option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001728 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001729 char *s = daemon->namebuff;
1730 for (*s = 0; netid; netid = netid->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001731 {
Simon Kelley9009d742008-11-14 20:04:27 +00001732 /* kill dupes. */
Simon Kelley7de060b2011-08-26 17:24:52 +01001733 struct dhcp_netid *n;
1734
Simon Kelley9009d742008-11-14 20:04:27 +00001735 for (n = netid->next; n; n = n->next)
1736 if (strcmp(netid->net, n->net) == 0)
1737 break;
1738
1739 if (!n)
1740 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001741 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001742 if (netid->next)
Simon Kelley1f15b812009-10-13 17:49:32 +01001743 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001744 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001745 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001746 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001747 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001748}
1749
1750static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1751{
1752 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1753 unsigned char *overload;
1754 size_t ret;
1755
1756 /* move agent_id back down to the end of the packet */
1757 if (agent_id)
1758 {
1759 memmove(p, agent_id, real_end - agent_id);
1760 p += real_end - agent_id;
1761 memset(p, 0, real_end - p); /* in case of overlap */
1762 }
1763
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001764 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001765 overload = find_overload(mess);
1766
1767 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001768 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001769 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001770 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001771 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001772 }
Simon Kelley28866e92011-02-14 20:19:14 +00001773 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001774 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1775
1776 if (overload && (option_uint(overload, 0, 1) & 2))
1777 {
1778 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001779 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001780 log_options(mess->sname, mess->xid);
1781 }
Simon Kelley28866e92011-02-14 20:19:14 +00001782 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001783 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1784
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001785
1786 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001787
Simon Kelley28866e92011-02-14 20:19:14 +00001788 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001789 {
1790 if (mess->siaddr.s_addr != 0)
1791 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1792
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001793 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1794 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1795
Simon Kelley7622fc02009-06-04 20:32:05 +01001796 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1797 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001798
1799 ret = (size_t)(p - (unsigned char *)mess);
1800
1801 if (ret < MIN_PACKETSZ)
1802 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001803
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001804 return ret;
1805}
1806
1807static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1808{
1809 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1810
1811 if (p + len + 3 >= end)
1812 /* not enough space in options area, try and use overload, if poss */
1813 {
1814 unsigned char *overload;
1815
1816 if (!(overload = find_overload(mess)) &&
1817 (mess->file[0] == 0 || mess->sname[0] == 0))
1818 {
1819 /* attempt to overload fname and sname areas, we've reserved space for the
1820 overflow option previuously. */
1821 overload = p;
1822 *(p++) = OPTION_OVERLOAD;
1823 *(p++) = 1;
1824 }
1825
1826 p = NULL;
1827
1828 /* using filename field ? */
1829 if (overload)
1830 {
1831 if (mess->file[0] == 0)
1832 overload[2] |= 1;
1833
1834 if (overload[2] & 1)
1835 {
1836 p = dhcp_skip_opts(mess->file);
1837 if (p + len + 3 >= mess->file + sizeof(mess->file))
1838 p = NULL;
1839 }
1840
1841 if (!p)
1842 {
1843 /* try to bring sname into play (it may be already) */
1844 if (mess->sname[0] == 0)
1845 overload[2] |= 2;
1846
1847 if (overload[2] & 2)
1848 {
1849 p = dhcp_skip_opts(mess->sname);
1850 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1851 p = NULL;
1852 }
1853 }
1854 }
1855
1856 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001857 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001858 }
1859
1860 if (p)
1861 {
1862 *(p++) = opt;
1863 *(p++) = len;
1864 }
1865
1866 return p;
1867}
1868
1869static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1870{
1871 int i;
1872 unsigned char *p = free_space(mess, end, opt, len);
1873
1874 if (p)
1875 for (i = 0; i < len; i++)
1876 *(p++) = val >> (8 * (len - (i + 1)));
1877}
1878
1879static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1880 char *string, int null_term)
1881{
1882 unsigned char *p;
1883 size_t len = strlen(string);
1884
1885 if (null_term && len != 255)
1886 len++;
1887
1888 if ((p = free_space(mess, end, opt, len)))
1889 memcpy(p, string, len);
1890}
1891
1892/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001893static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001894{
1895 int len = opt->len;
1896
1897 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1898 len++;
1899
1900 if (p && len != 0)
1901 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001902 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001903 {
1904 int j;
1905 struct in_addr *a = (struct in_addr *)opt->val;
1906 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1907 {
1908 /* zero means "self" (but not in vendorclass options.) */
1909 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001910 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001911 else
1912 memcpy(p, a, INADDRSZ);
1913 p += INADDRSZ;
1914 }
1915 }
1916 else
1917 memcpy(p, opt->val, len);
1918 }
1919 return len;
1920}
Simon Kelley7622fc02009-06-04 20:32:05 +01001921
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001922static int in_list(unsigned char *list, int opt)
1923{
1924 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001925
1926 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001927 if (!list)
1928 return 1;
1929
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001930 for (i = 0; list[i] != OPTION_END; i++)
1931 if (opt == list[i])
1932 return 1;
1933
1934 return 0;
1935}
1936
Simon Kelley7de060b2011-08-26 17:24:52 +01001937static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001938{
Simon Kelley7de060b2011-08-26 17:24:52 +01001939 struct dhcp_opt *opts;
1940
1941 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1942 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1943 return opts;
1944
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001945 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001946}
1947
Simon Kelley6b010842007-02-12 20:32:07 +00001948/* mark vendor-encapsulated options which match the client-supplied or
1949 config-supplied vendor class */
1950static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1951{
1952 for (; dopt; dopt = dopt->next)
1953 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001954 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001955 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001956 {
1957 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001958 if (dopt->u.vendor_class)
1959 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001960 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001961 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001962 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001963 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001964 break;
1965 }
1966 }
1967 }
1968}
1969
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001970static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1971 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001972{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001973 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001974 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001975 unsigned char *p;
1976
1977 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001978 for (enc_len = 0, start = opt; opt; opt = opt->next)
1979 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001980 {
1981 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001982 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001983 if (enc_len + new <= 255)
1984 enc_len += new;
1985 else
1986 {
1987 p = free_space(mess, end, encap, enc_len);
1988 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001989 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001990 {
1991 len = do_opt(start, p + 2, NULL, null_term);
1992 *(p++) = start->opt;
1993 *(p++) = len;
1994 p += len;
1995 }
1996 enc_len = new;
1997 start = opt;
1998 }
1999 }
2000
2001 if (enc_len != 0 &&
2002 (p = free_space(mess, end, encap, enc_len + 1)))
2003 {
2004 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002005 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002006 {
2007 len = do_opt(start, p + 2, NULL, null_term);
2008 *(p++) = start->opt;
2009 *(p++) = len;
2010 p += len;
2011 }
2012 *p = OPTION_END;
2013 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002014
2015 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002016}
2017
Simon Kelley7622fc02009-06-04 20:32:05 +01002018static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002019{
Simon Kelley7622fc02009-06-04 20:32:05 +01002020 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002021
Simon Kelley7622fc02009-06-04 20:32:05 +01002022 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2023 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2024 memcpy(p, uuid, 17);
2025}
2026
2027static int prune_vendor_opts(struct dhcp_netid *netid)
2028{
2029 int force = 0;
2030 struct dhcp_opt *opt;
2031
2032 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2033 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2034 if (opt->flags & DHOPT_VENDOR_MATCH)
2035 {
2036 if (!match_netid(opt->netid, netid, 1))
2037 opt->flags &= ~DHOPT_VENDOR_MATCH;
2038 else if (opt->flags & DHOPT_FORCE)
2039 force = 1;
2040 }
2041 return force;
2042}
2043
Simon Kelley316e2732010-01-22 20:16:09 +00002044static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local)
Simon Kelley7622fc02009-06-04 20:32:05 +01002045{
2046#define NUM_OPTS 4
2047
2048 unsigned char *p, *q;
2049 struct pxe_service *service;
2050 static struct dhcp_opt *o, *ret;
2051 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002052 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002053
2054 /* We pass back references to these, hence they are declared static */
2055 static unsigned char discovery_control;
2056 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2057 static struct dhcp_opt *fake_opts = NULL;
2058
Simon Kelley316e2732010-01-22 20:16:09 +00002059 /* Disable multicast, since we don't support it, and broadcast
2060 unless we need it */
2061 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002062
2063 ret = daemon->dhcp_opts;
2064
2065 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2066 return ret;
2067
2068 for (i = 0; i < NUM_OPTS; i++)
2069 {
2070 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2071 fake_opts[i].netid = NULL;
2072 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2073 }
2074
2075 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2076 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002077 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002078
2079 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2080 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2081 {
2082 size_t len = strlen(service->menu);
2083 /* opt 43 max size is 255. encapsulated option has type and length
2084 bytes, so its max size is 253. */
2085 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2086 {
2087 *(p++) = service->type >> 8;
2088 *(p++) = service->type;
2089 *(p++) = len;
2090 memcpy(p, service->menu, len);
2091 p += len;
2092 i++;
2093 }
2094 else
2095 {
2096 toobig:
2097 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2098 return daemon->dhcp_opts;
2099 }
2100
Simon Kelley316e2732010-01-22 20:16:09 +00002101 boot_server = service->basename ? local : service->server;
2102
2103 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002104 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002105 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002106 goto toobig;
2107
2108 /* Boot service with known address - give it */
2109 *(q++) = service->type >> 8;
2110 *(q++) = service->type;
2111 *(q++) = 1;
2112 /* dest misaligned */
2113 memcpy(q, &boot_server.s_addr, INADDRSZ);
2114 q += INADDRSZ;
2115 }
2116 else if (service->type != 0)
2117 /* We don't know the server for a service type, so we'll
2118 allow the client to broadcast for it */
2119 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002120 }
2121
2122 /* if no prompt, wait forever if there's a choice */
2123 fake_prompt[0] = (i > 1) ? 255 : 0;
2124
2125 if (i == 0)
2126 discovery_control = 8; /* no menu - just use use mess->filename */
2127 else
2128 {
2129 ret = &fake_opts[j--];
2130 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2131 ret->val = (unsigned char *)daemon->dhcp_buff;
2132 ret->opt = SUBOPT_PXE_MENU;
2133
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002134 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002135 {
2136 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002137 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2138 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002139 ret->opt = SUBOPT_PXE_SERVERS;
2140 }
2141 }
2142
2143 for (o = daemon->dhcp_opts; o; o = o->next)
2144 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2145 break;
2146
2147 if (!o)
2148 {
2149 ret = &fake_opts[j--];
2150 ret->len = sizeof(fake_prompt);
2151 ret->val = fake_prompt;
2152 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2153 }
2154
Simon Kelley316e2732010-01-22 20:16:09 +00002155 ret = &fake_opts[j--];
2156 ret->len = 1;
2157 ret->opt = SUBOPT_PXE_DISCOVERY;
2158 ret->val= &discovery_control;
2159
Simon Kelley7622fc02009-06-04 20:32:05 +01002160 return ret;
2161}
2162
2163static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2164{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002165 memset(mess->sname, 0, sizeof(mess->sname));
2166 memset(mess->file, 0, sizeof(mess->file));
2167 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2168 mess->siaddr.s_addr = 0;
2169}
Simon Kelleycdeda282006-03-16 20:16:06 +00002170
Simon Kelley7622fc02009-06-04 20:32:05 +01002171struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002172{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002173 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002174
2175 /* decide which dhcp-boot option we're using */
2176 for (boot = daemon->boot_config; boot; boot = boot->next)
2177 if (match_netid(boot->netid, netid, 0))
2178 break;
2179 if (!boot)
2180 /* No match, look for one without a netid */
2181 for (boot = daemon->boot_config; boot; boot = boot->next)
2182 if (match_netid(boot->netid, netid, 1))
2183 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002184
2185 return boot;
2186}
2187
2188static void do_options(struct dhcp_context *context,
2189 struct dhcp_packet *mess,
2190 unsigned char *end,
2191 unsigned char *req_options,
2192 char *hostname,
2193 char *domain, char *config_domain,
2194 struct dhcp_netid *netid,
2195 struct in_addr subnet_addr,
2196 unsigned char fqdn_flags,
2197 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002198 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002199 int vendor_class_len,
2200 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002201{
2202 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2203 struct dhcp_boot *boot;
2204 unsigned char *p;
2205 int i, len, force_encap = 0;
2206 unsigned char f0 = 0, s0 = 0;
2207 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002208 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002209 struct dhcp_netid *tagif;
2210 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002211
Simon Kelley7de060b2011-08-26 17:24:52 +01002212 /* flag options which are valid with the current tag set (sans context tags) */
2213 tagif = run_tag_if(netid);
2214 for (opt = config_opts; opt; opt = opt->next)
2215 {
2216 opt->flags &= ~DHOPT_TAGOK;
2217 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
2218 match_netid(opt->netid, tagif, 0))
2219 opt->flags |= DHOPT_TAGOK;
2220 }
2221
2222 /* now flag options which are valid, including the context tags,
2223 otherwise valid options are inhibited if we found a higher priotity one above */
2224 if (context && context->netid.net)
2225 {
2226 context->netid.next = netid;
2227 tagif = run_tag_if(&context->netid);
2228
2229 for (opt = config_opts; opt; opt = opt->next)
2230 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
2231 match_netid(opt->netid, tagif, 0))
2232 {
2233 struct dhcp_opt *tmp;
2234 for (tmp = config_opts; tmp; tmp = tmp->next)
2235 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
2236 break;
2237 if (!tmp)
2238 opt->flags |= DHOPT_TAGOK;
2239 }
2240 }
2241
2242 /* now flag untagged options which are not overridden by tagged ones */
2243 for (opt = config_opts; opt; opt = opt->next)
2244 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
2245 {
2246 struct dhcp_opt *tmp;
2247 for (tmp = config_opts; tmp; tmp = tmp->next)
2248 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
2249 break;
2250 if (!tmp)
2251 opt->flags |= DHOPT_TAGOK;
2252 else if (!tmp->netid)
2253 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
2254 }
2255
Simon Kelley7622fc02009-06-04 20:32:05 +01002256 if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
2257 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
Simon Kelley1ab84e22004-01-29 16:48:35 +00002258
Simon Kelley7622fc02009-06-04 20:32:05 +01002259 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002260 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002261 {
2262 char *q = daemon->namebuff;
2263 for (i = 0; req_options[i] != OPTION_END; i++)
2264 {
2265 char *s = option_string(req_options[i], NULL, NULL);
2266 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2267 "%d%s%s%s",
2268 req_options[i],
2269 s ? ":" : "",
2270 s ? s : "",
2271 req_options[i+1] == OPTION_END ? "" : ", ");
2272 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2273 {
2274 q = daemon->namebuff;
2275 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2276 }
2277 }
2278 }
2279
Simon Kelley7de060b2011-08-26 17:24:52 +01002280 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2281 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2282 break;
2283 if (id_list)
2284 mess->flags |= htons(0x8000); /* force broadcast */
2285
Simon Kelley73a08a22009-02-05 20:28:08 +00002286 if (context)
2287 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002288
2289 /* See if we can send the boot stuff as options.
2290 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002291 and very old DHCP clients won't have this, we also
2292 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002293 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002294 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002295 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002296 {
2297 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002298 {
Simon Kelley28866e92011-02-14 20:19:14 +00002299 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002300 req_options &&
2301 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002302 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2303 else
Simon Kelley824af852008-02-12 20:43:05 +00002304 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002305 }
2306
2307 if (boot->file)
2308 {
Simon Kelley28866e92011-02-14 20:19:14 +00002309 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002310 req_options &&
2311 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002312 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2313 else
Simon Kelley824af852008-02-12 20:43:05 +00002314 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002315 }
2316
Simon Kelley7de060b2011-08-26 17:24:52 +01002317 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002318 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002319 else if (boot->tftp_sname)
2320 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002321 }
Simon Kelley824af852008-02-12 20:43:05 +00002322 else
2323 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002324 they're not explicitly asked for as options. OPTION_END is used
2325 as an internal way to specify siaddr without using dhcp-boot, for use in
2326 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002327 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002328 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002329 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002330 {
2331 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2332 done_file = 1;
2333 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002334
Simon Kelley824af852008-02-12 20:43:05 +00002335 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002336 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002337 {
2338 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2339 done_server = 1;
2340 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002341
Simon Kelley7de060b2011-08-26 17:24:52 +01002342 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002343 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002344 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002345
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002346 /* We don't want to do option-overload for BOOTP, so make the file and sname
2347 fields look like they are in use, even when they aren't. This gets restored
2348 at the end of this function. */
2349
Simon Kelley28866e92011-02-14 20:19:14 +00002350 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002351 {
2352 f0 = mess->file[0];
2353 mess->file[0] = 1;
2354 s0 = mess->sname[0];
2355 mess->sname[0] = 1;
2356 }
2357
2358 /* At this point, if mess->sname or mess->file are zeroed, they are available
2359 for option overload, reserve space for the overload option. */
2360 if (mess->file[0] == 0 || mess->sname[0] == 0)
2361 end -= 3;
2362
Simon Kelley3be34542004-09-11 19:12:13 +01002363 /* rfc3011 says this doesn't need to be in the requested options list. */
2364 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002365 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002366
2367 /* replies to DHCPINFORM may not have a valid context */
2368 if (context)
2369 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002370 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002371 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2372
2373 /* May not have a "guessed" broadcast address if we got no packets via a relay
2374 from this net yet (ie just unicast renewals after a restart */
2375 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002376 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002377 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2378
2379 /* Same comments as broadcast apply, and also may not be able to get a sensible
2380 default when using subnet select. User must configure by steam in that case. */
2381 if (context->router.s_addr &&
2382 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002383 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002384 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2385
2386 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002387 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002388 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2389 }
Simon Kelley3be34542004-09-11 19:12:13 +01002390
Simon Kelley9009d742008-11-14 20:04:27 +00002391 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002392 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002393 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002394
Simon Kelley824af852008-02-12 20:43:05 +00002395 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002396 if (hostname)
2397 {
Simon Kelley824af852008-02-12 20:43:05 +00002398 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002399 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002400 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002401
2402 if (fqdn_flags != 0)
2403 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002404 len = strlen(hostname) + 3;
2405
Simon Kelley3d8df262005-08-29 12:19:27 +01002406 if (fqdn_flags & 0x04)
2407 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002408 else if (null_term)
2409 len++;
2410
Simon Kelley9009d742008-11-14 20:04:27 +00002411 if (domain)
2412 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002413
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002414 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002415 {
Simon Kelley3d8df262005-08-29 12:19:27 +01002416 *(p++) = fqdn_flags;
2417 *(p++) = 255;
2418 *(p++) = 255;
2419
2420 if (fqdn_flags & 0x04)
2421 {
2422 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002423 if (domain)
2424 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002425 *p++ = 0;
2426 }
2427 else
2428 {
2429 memcpy(p, hostname, strlen(hostname));
2430 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002431 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002432 {
2433 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002434 memcpy(p, domain, strlen(domain));
2435 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002436 }
2437 if (null_term)
2438 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002439 }
2440 }
2441 }
2442 }
2443
Simon Kelley6b010842007-02-12 20:32:07 +00002444 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002445 {
Simon Kelley824af852008-02-12 20:43:05 +00002446 int optno = opt->opt;
2447
Simon Kelley7de060b2011-08-26 17:24:52 +01002448 /* netids match and not encapsulated? */
2449 if (!(opt->flags & DHOPT_TAGOK))
2450 continue;
2451
Simon Kelley6b010842007-02-12 20:32:07 +00002452 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002453 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002454 continue;
2455
2456 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002457 if (optno == OPTION_CLIENT_FQDN ||
2458 optno == OPTION_MAXMESSAGE ||
2459 optno == OPTION_OVERLOAD ||
2460 optno == OPTION_PAD ||
2461 optno == OPTION_END)
2462 continue;
2463
2464 if (optno == OPTION_SNAME && done_server)
2465 continue;
2466
2467 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002468 continue;
2469
Simon Kelley33820b72004-04-03 21:10:00 +01002470 /* For the options we have default values on
2471 dhc-option=<optionno> means "don't include this option"
2472 not "include a zero-length option" */
2473 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002474 (optno == OPTION_NETMASK ||
2475 optno == OPTION_BROADCAST ||
2476 optno == OPTION_ROUTER ||
2477 optno == OPTION_DNSSERVER ||
2478 optno == OPTION_DOMAINNAME ||
2479 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002480 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002481
2482 /* vendor-class comes from elsewhere for PXE */
2483 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2484 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002485
Simon Kelley7622fc02009-06-04 20:32:05 +01002486 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002487 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002488 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002489
Simon Kelley824af852008-02-12 20:43:05 +00002490 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002491 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002492 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002493 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2494
Simon Kelley6b010842007-02-12 20:32:07 +00002495 /* If we send a vendor-id, revisit which vendor-ops we consider
2496 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002497 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002498 {
2499 match_vendor_opts(p - 2, config_opts);
2500 done_vendor_class = 1;
2501 }
Simon Kelley6b010842007-02-12 20:32:07 +00002502 }
2503 }
2504
Simon Kelley73a08a22009-02-05 20:28:08 +00002505 /* Now send options to be encapsulated in arbitrary options,
2506 eg dhcp-option=encap:172,17,.......
Simon Kelley316e2732010-01-22 20:16:09 +00002507 Also hand vendor-identifying vendor-encapsulated options,
2508 dhcp-option = rfc3925-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002509 The may be more that one "outer" to do, so group
2510 all the options which match each outer in turn. */
2511 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002512 opt->flags &= ~DHOPT_ENCAP_DONE;
2513
2514 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002515 {
2516 int flags;
2517
2518 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2519 {
2520 int found = 0;
2521 struct dhcp_opt *o;
2522
2523 if (opt->flags & DHOPT_ENCAP_DONE)
2524 continue;
2525
2526 for (len = 0, o = config_opts; o; o = o->next)
2527 {
2528 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2529
2530 o->flags &= ~DHOPT_ENCAP_MATCH;
2531
2532 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2533 continue;
2534
2535 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002536 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002537 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2538 {
2539 o->flags |= DHOPT_ENCAP_MATCH;
2540 found = 1;
2541 len += do_opt(o, NULL, NULL, 0) + 2;
2542 }
2543 }
2544
2545 if (found)
2546 {
2547 if (flags & DHOPT_ENCAPSULATE)
2548 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2549 else if (len > 250)
2550 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2551 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2552 {
2553 int swap_ent = htonl(opt->u.encap);
2554 memcpy(p, &swap_ent, 4);
2555 p += 4;
2556 *(p++) = len;
2557 for (o = config_opts; o; o = o->next)
2558 if (o->flags & DHOPT_ENCAP_MATCH)
2559 {
2560 len = do_opt(o, p + 2, NULL, 0);
2561 *(p++) = o->opt;
2562 *(p++) = len;
2563 p += len;
2564 }
2565 }
2566 }
2567 }
2568 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002569
Simon Kelley7de060b2011-08-26 17:24:52 +01002570 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002571
Simon Kelley316e2732010-01-22 20:16:09 +00002572 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002573 {
2574 pxe_misc(mess, end, uuid);
Simon Kelley7de060b2011-08-26 17:24:52 +01002575 config_opts = pxe_opts(pxe_arch, tagif, context->local);
Simon Kelley7622fc02009-06-04 20:32:05 +01002576 }
2577
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002578 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2579 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2580 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2581 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2582 /* If we send vendor encapsulated options, and haven't already sent option 60,
2583 echo back the value we got from the client. */
2584 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2585
Simon Kelley7622fc02009-06-04 20:32:05 +01002586 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002587 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002588 {
2589 mess->file[0] = f0;
2590 mess->sname[0] = s0;
2591 }
2592}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002593
Simon Kelley7622fc02009-06-04 20:32:05 +01002594#endif
2595
2596
2597
2598
2599
2600
2601