blob: 673ca3618cc202323ba84546378b700162863887 [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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 Kelley751d6f42012-02-10 15:24:51 +000066static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +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 &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000484 (!(lease = lease4_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 Kelleyc72daea2012-01-05 21:33:27 +0000524 if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
525 fqdn_flags |= 0x03;
526
Simon Kelley3d8df262005-08-29 12:19:27 +0100527 fqdn_flags &= ~0x08;
Simon Kelley3d8df262005-08-29 12:19:27 +0100528
529 if (fqdn_flags & 0x04)
530 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
531 {
532 memcpy(pq, op+1, *op);
533 pq += *op;
534 op += (*op)+1;
535 *(pq++) = '.';
536 }
537 else
538 {
539 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000540 if (len > 0 && op[len-1] == 0)
541 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100542 pq += len + 1;
543 }
544
545 if (pq != daemon->dhcp_buff)
546 pq--;
547
548 *pq = 0;
549
Simon Kelley1f15b812009-10-13 17:49:32 +0100550 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100551 offer_hostname = client_hostname = daemon->dhcp_buff;
552 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000553 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000554 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000555 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100556 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000557 /* Microsoft clients are broken, and need zero-terminated strings
558 in options. We detect this state here, and do the same in
559 any options we send */
560 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
561 borken_opt = 1;
562 else
563 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100564 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100565 client_hostname = daemon->dhcp_buff;
566 }
567
Simon Kelley28866e92011-02-14 20:19:14 +0000568 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100569 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
570
Simon Kelley3d8df262005-08-29 12:19:27 +0100571 if (have_config(config, CONFIG_NAME))
572 {
573 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000574 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000575 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000576 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100577 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000578 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100579 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100580 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100581 {
Simon Kelley9009d742008-11-14 20:04:27 +0000582 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100583
584 if (strlen(client_hostname) != 0)
585 {
586 hostname = client_hostname;
587 if (!config)
588 {
589 /* Search again now we have a hostname.
590 Only accept configs without CLID and HWADDR here, (they won't match)
591 to avoid impersonation by name. */
592 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
593 mess->chaddr, mess->hlen,
594 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000595 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000596 {
597 config = new;
598 /* set "known" tag for known hosts */
599 known_id.net = "known";
600 known_id.next = netid;
601 netid = &known_id;
602 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100603 }
604 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000605 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100606
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100607 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100608 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100609 struct dhcp_netid_list *list;
610
611 for (list = config->netid; list; list = list->next)
612 {
613 list->list->next = netid;
614 netid = list->list;
615 }
Simon Kelleya2226412004-05-13 20:27:08 +0100616 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100617
Simon Kelley73a08a22009-02-05 20:28:08 +0000618 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
619 Otherwise assume the option is an array, and look for a matching element.
Simon Kelley316e2732010-01-22 20:16:09 +0000620 If no data given, existance of the option is enough. This code handles
621 rfc3925 V-I classes too. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000622 for (o = daemon->dhcp_match; o; o = o->next)
623 {
Simon Kelley316e2732010-01-22 20:16:09 +0000624 unsigned int len, elen, match = 0;
625 size_t offset, o2;
Simon Kelley73a08a22009-02-05 20:28:08 +0000626
Simon Kelley316e2732010-01-22 20:16:09 +0000627 if (o->flags & DHOPT_RFC3925)
628 {
629 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
630 continue;
631
632 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
633 {
634 len = option_uint(opt, offset + 4 , 1);
635 /* Need to take care that bad data can't run us off the end of the packet */
636 if ((offset + len + 5 <= (option_len(opt))) &&
637 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
638 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
639 {
640 elen = option_uint(opt, o2, 1);
641 if ((o2 + elen + 1 <= option_len(opt)) &&
642 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
643 break;
644 }
645 if (match)
646 break;
647 }
648 }
649 else
650 {
651 if (!(opt = option_find(mess, sz, o->opt, 1)))
652 continue;
653
654 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
655 }
656
657 if (match)
Simon Kelley73a08a22009-02-05 20:28:08 +0000658 {
659 o->netid->next = netid;
660 netid = o->netid;
661 }
662 }
663
Simon Kelley26128d22004-11-14 16:43:54 +0000664 /* user-class options are, according to RFC3004, supposed to contain
665 a set of counted strings. Here we check that this is so (by seeing
666 if the counts are consistent with the overall option length) and if
667 so zero the counts so that we don't get spurious matches between
668 the vendor string and the counts. If the lengths don't add up, we
669 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100670 and just do the substring match. dhclient provides these broken options.
671 The code, later, which sends user-class data to the lease-change script
672 relies on the transformation done here.
673 */
Simon Kelleya2226412004-05-13 20:27:08 +0100674
Simon Kelleybb01cb92004-12-13 20:56:23 +0000675 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100676 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100677 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000678 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000679 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
680 if (j == option_len(opt))
681 for (j = 0; j < option_len(opt); j = tmp)
682 {
683 tmp = j + ucp[j] + 1;
684 ucp[j] = 0;
685 }
Simon Kelleya2226412004-05-13 20:27:08 +0100686 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000687
Simon Kelley26128d22004-11-14 16:43:54 +0000688 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100689 {
690 int mopt;
691
692 if (vendor->match_type == MATCH_VENDOR)
693 mopt = OPTION_VENDOR_ID;
694 else if (vendor->match_type == MATCH_USER)
695 mopt = OPTION_USER_CLASS;
696 else
697 continue;
698
699 if ((opt = option_find(mess, sz, mopt, 1)))
700 {
701 int i;
702 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100703 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100704 {
705 vendor->netid.next = netid;
706 netid = &vendor->netid;
707 break;
708 }
709 }
710 }
711
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100712 /* mark vendor-encapsulated options which match the client-supplied vendor class,
713 save client-supplied vendor class */
714 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
715 {
716 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
717 vendor_class_len = option_len(opt);
718 }
719 match_vendor_opts(opt, daemon->dhcp_opts);
720
Simon Kelley28866e92011-02-14 20:19:14 +0000721 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100722 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100723 if (sanitise(opt, daemon->namebuff))
724 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100725 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100726 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100727 }
728
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100729 tagif_netid = run_tag_if(netid);
730
Simon Kelley26128d22004-11-14 16:43:54 +0000731 /* if all the netids in the ignore list are present, ignore this client */
732 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100733 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000734 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100735
736 /* If configured, we can override the server-id to be the address of the relay,
737 so that all traffic goes via the relay and can pick up agent-id info. This can be
738 configured for all relays, or by address. */
739 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
740 {
741 if (!daemon->override_relays)
742 override = mess->giaddr;
743 else
744 {
745 struct addr_list *l;
746 for (l = daemon->override_relays; l; l = l->next)
747 if (l->addr.s_addr == mess->giaddr.s_addr)
748 break;
749 if (l)
750 override = mess->giaddr;
751 }
752 }
753
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100754 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
755 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000756 clid = NULL;
757
Simon Kelley7622fc02009-06-04 20:32:05 +0100758 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100759 if (daemon->enable_pxe &&
760 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100761 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
762 {
763 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
764 {
765 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
766 uuid = pxe_uuid;
767 }
768
769 /* Check if this is really a PXE bootserver request, and handle specially if so. */
770 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
771 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
772 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
773 {
774 struct pxe_service *service;
775 int type = option_uint(opt, 0, 2);
776 int layer = option_uint(opt, 2, 2);
777 unsigned char save71[4];
778 struct dhcp_opt opt71;
779
Simon Kelley1f15b812009-10-13 17:49:32 +0100780 if (ignore)
781 return 0;
782
Simon Kelley7622fc02009-06-04 20:32:05 +0100783 if (layer & 0x8000)
784 {
785 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
786 return 0;
787 }
788
789 memcpy(save71, option_ptr(opt, 0), 4);
790
791 for (service = daemon->pxe_services; service; service = service->next)
792 if (service->type == type)
793 break;
794
795 if (!service || !service->basename)
796 return 0;
797
798 clear_packet(mess, end);
799
800 mess->yiaddr = mess->ciaddr;
801 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000802 if (service->sname)
803 mess->siaddr = a_record_from_hosts(service->sname, now);
804 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100805 mess->siaddr = service->server;
806 else
807 mess->siaddr = context->local;
808
809 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
810 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
811 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
812 pxe_misc(mess, end, uuid);
813
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100814 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100815 opt71.val = save71;
816 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
817 opt71.len = 4;
818 opt71.flags = DHOPT_VENDOR_MATCH;
819 opt71.netid = NULL;
820 opt71.next = daemon->dhcp_opts;
821 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
822
823 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100824 log_tags(tagif_netid, mess);
825 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100826 }
827
828 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
829 {
830 pxearch = option_uint(opt, 0, 2);
831
Simon Kelley316e2732010-01-22 20:16:09 +0000832 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000833 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100834 {
Simon Kelley28866e92011-02-14 20:19:14 +0000835 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100836
Simon Kelley28866e92011-02-14 20:19:14 +0000837 for (tmp = context; tmp; tmp = tmp->current)
838 if ((tmp->flags & CONTEXT_PROXY) &&
839 match_netid(tmp->filter, tagif_netid, 1))
840 break;
841
842 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100843 {
Simon Kelley28866e92011-02-14 20:19:14 +0000844 struct dhcp_boot *boot = find_boot(tagif_netid);
845
846 mess->yiaddr.s_addr = 0;
847 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
848 {
849 mess->ciaddr.s_addr = 0;
850 mess->flags |= htons(0x8000); /* broadcast */
851 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100852
Simon Kelley28866e92011-02-14 20:19:14 +0000853 clear_packet(mess, end);
854
855 /* Provide the bootfile here, for gPXE, and in case we have no menu items
856 and set discovery_control = 8 */
857 if (boot)
858 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100859 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000860 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100861 else if (boot->tftp_sname)
862 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000863
864 if (boot->file)
865 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
866 }
867
868 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
869 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
870 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
871 pxe_misc(mess, end, uuid);
872 prune_vendor_opts(tagif_netid);
Simon Kelley751d6f42012-02-10 15:24:51 +0000873 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
Simon Kelley28866e92011-02-14 20:19:14 +0000874
875 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100876 log_tags(tagif_netid, mess);
877 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100878 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100879 }
880 }
881 }
882
883 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000884 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100885 return 0;
886
Simon Kelleybb01cb92004-12-13 20:56:23 +0000887 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100888 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100889 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100890 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000891 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100892 }
893
Simon Kelley3be34542004-09-11 19:12:13 +0100894 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000895 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000896 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000897 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000898 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000899 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100900
Simon Kelley44a2a312004-03-10 20:04:35 +0000901 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100902 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000903
Simon Kelleybb01cb92004-12-13 20:56:23 +0000904 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000905 return 0;
906
Simon Kelley7622fc02009-06-04 20:32:05 +0100907 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000908
909 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
910 lease_prune(lease, now);
911
Simon Kelley33820b72004-04-03 21:10:00 +0100912 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000913 config->addr.s_addr == option_addr(opt).s_addr)
914 {
Simon Kelley849a8352006-06-09 21:02:31 +0100915 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100916 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100917 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100918 config->flags |= CONFIG_DECLINED;
919 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000920 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100921 else
922 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100923 for (; context; context = context->current)
924 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000925
926 return 0;
927
928 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100929 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100930 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000931 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000932 return 0;
933
Simon Kelley44a2a312004-03-10 20:04:35 +0000934 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
935 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100936 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000937 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100938
Simon Kelley7622fc02009-06-04 20:32:05 +0100939 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000940
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000941 return 0;
942
943 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100944 if (ignore || have_config(config, CONFIG_DISABLE))
945 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000946 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100947 opt = NULL;
948 }
949 else
950 {
951 struct in_addr addr, conf;
952
Simon Kelley1a6bca82008-07-11 11:11:42 +0100953 addr.s_addr = conf.s_addr = 0;
954
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100955 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
956 addr = option_addr(opt);
957
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100958 if (have_config(config, CONFIG_ADDR))
959 {
Simon Kelley849a8352006-06-09 21:02:31 +0100960 char *addrs = inet_ntoa(config->addr);
961
Simon Kelley9009d742008-11-14 20:04:27 +0000962 if ((ltmp = lease_find_by_addr(config->addr)) &&
963 ltmp != lease &&
964 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000965 {
966 int len;
967 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
968 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100969 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100970 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000971 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100972 else
973 {
974 struct dhcp_context *tmp;
975 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100976 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100977 break;
978 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100979 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 +0100980 else if (have_config(config, CONFIG_DECLINED) &&
981 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100982 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100983 else
984 conf = config->addr;
985 }
986 }
987
988 if (conf.s_addr)
989 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100990 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100991 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100992 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100993 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100994 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100995 !config_find_by_address(daemon->dhcp_conf, addr))
996 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100997 else if (emac_len == 0)
998 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100999 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001000 message = _("no address available");
1001 }
1002
Simon Kelley7622fc02009-06-04 20:32:05 +01001003 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001004
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001005 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001006 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001007
Simon Kelleycdeda282006-03-16 20:16:06 +00001008 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001009 {
1010 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001011 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001012 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001013
1014 log_tags(tagif_netid, mess);
1015
1016 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1017
Simon Kelley824af852008-02-12 20:43:05 +00001018 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001019 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001020 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001021 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001022 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001023 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001024 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001025 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001026 option_put(mess, end, OPTION_T1, 4, (time/2));
1027 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001028 }
Simon Kelley9009d742008-11-14 20:04:27 +00001029 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001030 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001031
Simon Kelley7de060b2011-08-26 17:24:52 +01001032 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001033
1034 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001035 if (ignore || have_config(config, CONFIG_DISABLE))
1036 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001037 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001038 {
1039 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001040 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001041
Simon Kelley4011c4e2006-10-28 16:26:19 +01001042 /* send vendor and user class info for new or recreated lease */
1043 do_classes = 1;
1044
Simon Kelleybb01cb92004-12-13 20:56:23 +00001045 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001046 {
Simon Kelley3be34542004-09-11 19:12:13 +01001047 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001048 selecting = 1;
1049
Simon Kelley1a6bca82008-07-11 11:11:42 +01001050 if (override.s_addr != 0)
1051 {
1052 if (option_addr(opt).s_addr != override.s_addr)
1053 return 0;
1054 }
Simon Kelley9009d742008-11-14 20:04:27 +00001055 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001056 {
1057 for (; context; context = context->current)
1058 if (context->local.s_addr == option_addr(opt).s_addr)
1059 break;
1060
1061 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001062 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001063 /* Handle very strange configs where clients have more than one route to the server.
1064 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1065 Have to set override to make sure we echo back the correct server-id */
1066 struct irec *intr;
1067
1068 enumerate_interfaces();
1069
1070 for (intr = daemon->interfaces; intr; intr = intr->next)
1071 if (intr->addr.sa.sa_family == AF_INET &&
1072 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1073 intr->tftp_ok)
1074 break;
1075
1076 if (intr)
1077 override = intr->addr.in.sin_addr;
1078 else
1079 {
1080 /* In auth mode, a REQUEST sent to the wrong server
1081 should be faulted, so that the client establishes
1082 communication with us, otherwise, silently ignore. */
1083 if (!option_bool(OPT_AUTHORITATIVE))
1084 return 0;
1085 message = _("wrong server-ID");
1086 }
Simon Kelley9009d742008-11-14 20:04:27 +00001087 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001088 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001089
Simon Kelley3be34542004-09-11 19:12:13 +01001090 /* If a lease exists for this host and another address, squash it. */
1091 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1092 {
1093 lease_prune(lease, now);
1094 lease = NULL;
1095 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001096 }
Simon Kelley3be34542004-09-11 19:12:13 +01001097 else
1098 {
1099 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001100 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001101 return 0;
1102
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001103 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001104 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001105 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001106 }
1107 else
1108 {
1109 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001110 /* Check existing lease for this address.
1111 We allow it to be missing if dhcp-authoritative mode
1112 as long as we can allocate the lease now - checked below.
1113 This makes for a smooth recovery from a lost lease DB */
1114 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001115 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001116 {
Simon Kelley28866e92011-02-14 20:19:14 +00001117 /* A client rebinding will broadcast the request, so we may see it even
1118 if the lease is held by another server. Just ignore it in that case.
1119 If the request is unicast to us, then somethings wrong, NAK */
1120 if (!unicast_dest)
1121 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001122 message = _("lease not found");
1123 /* ensure we broadcast NAK */
1124 unicast_dest = 0;
1125 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001126
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001127 /* desynchronise renewals */
1128 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001129 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001130 }
1131
Simon Kelley7622fc02009-06-04 20:32:05 +01001132 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001133
Simon Kelleydfa666f2004-08-02 18:27:27 +01001134 if (!message)
1135 {
1136 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001137 struct dhcp_context *tmp = NULL;
1138
1139 if (have_config(config, CONFIG_ADDR))
1140 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001141 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001142 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001143
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001144 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001145 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001146 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001147 message = _("wrong network");
1148 /* ensure we broadcast NAK */
1149 unicast_dest = 0;
1150 }
1151
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001152 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001153 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001154 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001155 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001156
Simon Kelleydfa666f2004-08-02 18:27:27 +01001157 /* Check if a new static address has been configured. Be very sure that
1158 when the client does DISCOVER, it will get the static address, otherwise
1159 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001160 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001161 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001162 (!have_config(config, CONFIG_DECLINED) ||
1163 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001164 config->addr.s_addr != mess->yiaddr.s_addr &&
1165 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001166 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001167
1168 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001169 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001170 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001171
Simon Kelley9009d742008-11-14 20:04:27 +00001172 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1173 {
1174 /* If a host is configured with more than one MAC address, it's OK to 'nix
1175 a lease from one of it's MACs to give the address to another. */
1176 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1177 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001178 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001179 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1180 inet_ntoa(ltmp->addr));
1181 lease = ltmp;
1182 }
Simon Kelley16972692006-10-16 20:04:18 +01001183 else
Simon Kelley9009d742008-11-14 20:04:27 +00001184 message = _("address in use");
1185 }
1186
1187 if (!message)
1188 {
1189 if (emac_len == 0)
1190 message = _("no unique-id");
1191
1192 else if (!lease)
1193 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001194 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001195 do_classes = 1;
1196 else
1197 message = _("no leases left");
1198 }
Simon Kelley16972692006-10-16 20:04:18 +01001199 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001200 }
Simon Kelley16972692006-10-16 20:04:18 +01001201
Simon Kelley44a2a312004-03-10 20:04:35 +00001202 if (message)
1203 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001204 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001205
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001206 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001207 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001208 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001209 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001210 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001211 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1212 a distant subnet which unicast a REQ to us won't work. */
1213 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1214 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1215 {
1216 mess->flags |= htons(0x8000); /* broadcast */
1217 mess->ciaddr.s_addr = 0;
1218 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001219 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001220 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001221 {
Simon Kelley316e2732010-01-22 20:16:09 +00001222 if (context->netid.net)
1223 {
1224 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001225 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001226 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001227
1228 log_tags(tagif_netid, mess);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001229
Simon Kelley316e2732010-01-22 20:16:09 +00001230#ifdef HAVE_SCRIPT
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001231 if (do_classes && daemon->lease_change_command)
1232 {
1233 struct dhcp_netid *n;
1234
1235 if (mess->giaddr.s_addr)
1236 lease->giaddr = mess->giaddr;
1237
1238 lease->changed = 1;
1239 free(lease->extradata);
Simon Kelley28866e92011-02-14 20:19:14 +00001240 lease->extradata = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001241 lease->extradata_size = lease->extradata_len = 0;
1242
1243 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1244 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1245 add_extradata_opt(lease, oui);
1246 add_extradata_opt(lease, serial);
1247 add_extradata_opt(lease, class);
1248
1249 /* space-concat tag set */
1250 if (!tagif_netid)
1251 add_extradata_opt(lease, NULL);
1252 else
1253 for (n = tagif_netid; n; n = n->next)
Simon Kelley39bec5f2012-01-06 22:36:58 +00001254 {
1255 struct dhcp_netid *n1;
1256 /* kill dupes */
1257 for (n1 = n->next; n1; n1 = n1->next)
1258 if (strcmp(n->net, n1->net) == 0)
1259 break;
1260 if (!n1)
1261 add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1262 }
1263
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001264 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1265 {
1266 int len = option_len(opt);
1267 unsigned char *ucp = option_ptr(opt, 0);
1268 /* If the user-class option started as counted strings, the first byte will be zero. */
1269 if (len != 0 && ucp[0] == 0)
1270 ucp++, len--;
1271 add_extradata_data(lease, ucp, len, 0);
1272 }
1273 }
Simon Kelley316e2732010-01-22 20:16:09 +00001274#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001275
1276 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1277 {
1278 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001279 hostname = client_hostname;
1280 hostname_auth = 1;
1281 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001282
Simon Kelley824af852008-02-12 20:43:05 +00001283 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +00001284 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001285
Simon Kelley832af0b2007-01-21 20:01:28 +00001286 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1287 if (!hostname_auth)
1288 {
1289 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001290 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001291 break;
1292 if (id_list)
1293 hostname = NULL;
1294 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001295
1296 /* Last ditch, if configured, generate hostname from mac address */
1297 if (!hostname && emac_len != 0)
1298 {
1299 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1300 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1301 break;
1302 if (id_list)
1303 {
1304 int i;
1305
1306 hostname = daemon->dhcp_buff;
1307 /* buffer is 256 bytes, 3 bytes per octet */
1308 for (i = 0; (i < emac_len) && (i < 80); i++)
1309 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1310 hostname = daemon->dhcp_buff;
1311 }
1312 }
1313
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001314 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +00001315 lease_set_hostname(lease, hostname, hostname_auth);
Simon Kelley832af0b2007-01-21 20:01:28 +00001316
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001317 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +00001318 lease_set_interface(lease, int_index);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001319
1320 if (override.s_addr != 0)
1321 lease->override = override;
1322 else
1323 override = lease->override;
1324
Simon Kelley7622fc02009-06-04 20:32:05 +01001325 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001326
Simon Kelley7622fc02009-06-04 20:32:05 +01001327 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001328 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001329 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001330 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001331 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001332 {
Simon Kelley59353a62004-11-21 19:34:28 +00001333 while (fuzz > (time/16))
1334 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001335 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1336 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001337 }
Simon Kelley9009d742008-11-14 20:04:27 +00001338 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001339 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001340 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001341
Simon Kelley7de060b2011-08-26 17:24:52 +01001342 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001343
1344 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001345 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001346 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001347
Simon Kelley7622fc02009-06-04 20:32:05 +01001348 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001349
Simon Kelley73a08a22009-02-05 20:28:08 +00001350 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001351 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001352
1353 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001354 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001355
Simon Kelley5aabfc72007-08-29 11:24:47 +01001356 /* Find a least based on IP address if we didn't
1357 get one from MAC address/client-d */
1358 if (!lease &&
1359 (lease = lease_find_by_addr(mess->ciaddr)) &&
1360 lease->hostname)
1361 hostname = lease->hostname;
1362
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001363 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1364 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001365
Simon Kelley73a08a22009-02-05 20:28:08 +00001366 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001367 {
1368 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001369 tagif_netid = run_tag_if( &context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001370 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001371
1372 log_tags(tagif_netid, mess);
1373
1374 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001375
Simon Kelley3927da42008-07-20 15:10:39 +01001376 if (lease)
1377 {
1378 if (override.s_addr != 0)
1379 lease->override = override;
1380 else
1381 override = lease->override;
1382 }
1383
Simon Kelley7622fc02009-06-04 20:32:05 +01001384 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001385 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001386 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1387
Simon Kelley5aabfc72007-08-29 11:24:47 +01001388 if (lease)
1389 {
1390 if (lease->expires == 0)
1391 time = 0xffffffff;
1392 else
1393 time = (unsigned int)difftime(lease->expires, now);
1394 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001395 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001396 }
Simon Kelley824af852008-02-12 20:43:05 +00001397
Simon Kelley9009d742008-11-14 20:04:27 +00001398 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001399 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001400
Simon Kelley5aabfc72007-08-29 11:24:47 +01001401 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001402 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001403 }
1404
1405 return 0;
1406}
1407
Simon Kelley316e2732010-01-22 20:16:09 +00001408static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
1409{
1410 int i;
1411
1412 if (o->len > len)
1413 return 0;
1414
1415 if (o->len == 0)
1416 return 1;
1417
1418 if (o->flags & DHOPT_HEX)
1419 {
1420 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
1421 return 1;
1422 }
1423 else
1424 for (i = 0; i <= (len - o->len); )
1425 {
1426 if (memcmp(o->val, p + i, o->len) == 0)
1427 return 1;
1428
1429 if (o->flags & DHOPT_STRING)
1430 i++;
1431 else
1432 i += o->len;
1433 }
1434
1435 return 0;
1436}
1437
1438
Simon Kelley6b010842007-02-12 20:32:07 +00001439/* find a good value to use as MAC address for logging and address-allocation hashing.
1440 This is normally just the chaddr field from the DHCP packet,
1441 but eg Firewire will have hlen == 0 and use the client-id instead.
1442 This could be anything, but will normally be EUI64 for Firewire.
1443 We assume that if the first byte of the client-id equals the htype byte
1444 then the client-id is using the usual encoding and use the rest of the
1445 client-id: if not we can use the whole client-id. This should give
1446 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001447unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001448 int clid_len, unsigned char *clid, int *len_out)
1449{
1450 if (hwlen == 0 && clid && clid_len > 3)
1451 {
1452 if (clid[0] == hwtype)
1453 {
1454 *len_out = clid_len - 1 ;
1455 return clid + 1;
1456 }
1457
1458#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1459 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1460 {
1461 *len_out = clid_len - 1 ;
1462 return clid + 1;
1463 }
1464#endif
1465
1466 *len_out = clid_len;
1467 return clid;
1468 }
1469
1470 *len_out = hwlen;
1471 return hwaddr;
1472}
1473
Simon Kelley824af852008-02-12 20:43:05 +00001474static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001475{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001476 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001477
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001478 if (opt)
1479 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001480 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001481 if (req_time < 120 )
1482 req_time = 120; /* sanity */
1483 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1484 time = req_time;
1485 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001486
1487 return time;
1488}
1489
Simon Kelley73a08a22009-02-05 20:28:08 +00001490static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001491{
Simon Kelley73a08a22009-02-05 20:28:08 +00001492 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001493 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001494 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001495 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001496 else
1497 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001498}
1499
Simon Kelleyf2621c72007-04-29 19:47:21 +01001500static int sanitise(unsigned char *opt, char *buf)
1501{
1502 char *p;
1503 int i;
1504
1505 *buf = 0;
1506
1507 if (!opt)
1508 return 0;
1509
Simon Kelley1a6bca82008-07-11 11:11:42 +01001510 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001511
1512 for (i = option_len(opt); i > 0; i--)
1513 {
1514 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001515 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001516 *buf++ = c;
1517 }
1518 *buf = 0; /* add terminator */
1519
1520 return 1;
1521}
1522
Simon Kelley316e2732010-01-22 20:16:09 +00001523#ifdef HAVE_SCRIPT
1524static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
1525{
1526 if ((lease->extradata_size - lease->extradata_len) < (len + 1))
1527 {
1528 size_t newsz = lease->extradata_len + len + 100;
1529 unsigned char *new = whine_malloc(newsz);
1530
1531 if (!new)
1532 return;
1533
1534 if (lease->extradata)
1535 {
1536 memcpy(new, lease->extradata, lease->extradata_len);
1537 free(lease->extradata);
1538 }
1539
1540 lease->extradata = new;
1541 lease->extradata_size = newsz;
1542 }
1543
1544 if (len != 0)
1545 memcpy(lease->extradata + lease->extradata_len, data, len);
1546 lease->extradata[lease->extradata_len + len] = delim;
1547 lease->extradata_len += len + 1;
1548}
1549
1550static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1551{
1552 if (!opt)
1553 add_extradata_data(lease, NULL, 0, 0);
1554 else
1555 {
1556 size_t i, len = option_len(opt);
1557 unsigned char *ucp = option_ptr(opt, 0);
1558
1559 /* check for embeded NULLs */
1560 for (i = 0; i < len; i++)
1561 if (ucp[i] == 0)
1562 {
1563 len = i;
1564 break;
1565 }
1566
1567 add_extradata_data(lease, ucp, len, 0);
1568 }
1569}
1570#endif
1571
Simon Kelley5aabfc72007-08-29 11:24:47 +01001572static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001573 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001574{
Simon Kelley16972692006-10-16 20:04:18 +01001575 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001576
Simon Kelley16972692006-10-16 20:04:18 +01001577 /* addr may be misaligned */
1578 if (addr)
1579 memcpy(&a, addr, sizeof(a));
1580
Simon Kelley7622fc02009-06-04 20:32:05 +01001581 print_mac(daemon->namebuff, ext_mac, mac_len);
1582
Simon Kelley28866e92011-02-14 20:19:14 +00001583 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001584 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1585 ntohl(xid),
1586 type,
1587 interface,
1588 addr ? inet_ntoa(a) : "",
1589 addr ? " " : "",
1590 daemon->namebuff,
1591 string ? string : "");
1592 else
1593 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1594 type,
1595 interface,
1596 addr ? inet_ntoa(a) : "",
1597 addr ? " " : "",
1598 daemon->namebuff,
1599 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001600}
1601
Simon Kelley7622fc02009-06-04 20:32:05 +01001602static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001603{
1604 while (*start != OPTION_END)
1605 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001606 int is_ip, is_name, i;
1607 char *text = option_string(start[0], &is_ip, &is_name);
1608 unsigned char trunc = option_len(start);
1609
1610 if (is_ip)
1611 for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ)
1612 {
1613 if (i != 0)
1614 strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
1615 strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
1616 }
1617 else if (!is_name || !sanitise(start, daemon->namebuff))
1618 {
1619 if (trunc > 13)
1620 trunc = 13;
1621 print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
1622 }
1623
1624 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
1625 ntohl(xid), option_len(start), start[0],
Simon Kelleyf2621c72007-04-29 19:47:21 +01001626 text ? ":" : "", text ? text : "",
Simon Kelley7622fc02009-06-04 20:32:05 +01001627 trunc == 0 ? "" : " ",
1628 trunc == 0 ? "" : daemon->namebuff,
1629 trunc == option_len(start) ? "" : "...");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001630 start += start[1] + 2;
1631 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001632}
1633
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001634static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001635{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001636 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001637 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001638 if (p > end)
1639 return NULL;
1640 else if (*p == OPTION_END)
1641 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001642 else if (*p == OPTION_PAD)
1643 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001644 else
1645 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001646 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001647 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001648 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001649 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001650 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001651 return NULL; /* malformed packet */
1652 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001653 return p;
1654 p += opt_len + 2;
1655 }
1656 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001657}
1658
Simon Kelleycdeda282006-03-16 20:16:06 +00001659static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001660{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001661 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001662
Simon Kelley3be34542004-09-11 19:12:13 +01001663 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001664 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1665 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001666
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001667 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001668 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001669 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001670
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001671 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001672 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001673 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1674 return ret;
1675
1676 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001677 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001678 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1679 return ret;
1680
1681 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001682}
1683
Simon Kelley7622fc02009-06-04 20:32:05 +01001684static struct in_addr option_addr_arr(unsigned char *opt, int offset)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001685{
1686 /* this worries about unaligned data in the option. */
1687 /* struct in_addr is network byte order */
1688 struct in_addr ret;
1689
Simon Kelley7622fc02009-06-04 20:32:05 +01001690 memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001691
1692 return ret;
1693}
1694
Simon Kelley7622fc02009-06-04 20:32:05 +01001695static struct in_addr option_addr(unsigned char *opt)
1696{
1697 return option_addr_arr(opt, 0);
1698}
1699
1700static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001701{
1702 /* this worries about unaligned data and byte order */
1703 unsigned int ret = 0;
1704 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001705 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001706
1707 for (i = 0; i < size; i++)
1708 ret = (ret << 8) | *p++;
1709
1710 return ret;
1711}
1712
1713static unsigned char *dhcp_skip_opts(unsigned char *start)
1714{
1715 while (*start != 0)
1716 start += start[1] + 2;
1717 return start;
1718}
1719
1720/* only for use when building packet: doesn't check for bad data. */
1721static unsigned char *find_overload(struct dhcp_packet *mess)
1722{
1723 unsigned char *p = &mess->options[0] + sizeof(u32);
1724
1725 while (*p != 0)
1726 {
1727 if (*p == OPTION_OVERLOAD)
1728 return p;
1729 p += p[1] + 2;
1730 }
1731 return NULL;
1732}
1733
Simon Kelley7de060b2011-08-26 17:24:52 +01001734static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001735{
Simon Kelley28866e92011-02-14 20:19:14 +00001736 if (netid && option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001737 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001738 char *s = daemon->namebuff;
1739 for (*s = 0; netid; netid = netid->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001740 {
Simon Kelley9009d742008-11-14 20:04:27 +00001741 /* kill dupes. */
Simon Kelley7de060b2011-08-26 17:24:52 +01001742 struct dhcp_netid *n;
1743
Simon Kelley9009d742008-11-14 20:04:27 +00001744 for (n = netid->next; n; n = n->next)
1745 if (strcmp(netid->net, n->net) == 0)
1746 break;
1747
1748 if (!n)
1749 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001750 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001751 if (netid->next)
Simon Kelley1f15b812009-10-13 17:49:32 +01001752 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001753 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001754 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001755 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001756 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001757}
1758
1759static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1760{
1761 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1762 unsigned char *overload;
1763 size_t ret;
1764
1765 /* move agent_id back down to the end of the packet */
1766 if (agent_id)
1767 {
1768 memmove(p, agent_id, real_end - agent_id);
1769 p += real_end - agent_id;
1770 memset(p, 0, real_end - p); /* in case of overlap */
1771 }
1772
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001773 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001774 overload = find_overload(mess);
1775
1776 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001777 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001778 *dhcp_skip_opts(mess->file) = 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->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001781 }
Simon Kelley28866e92011-02-14 20:19:14 +00001782 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001783 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1784
1785 if (overload && (option_uint(overload, 0, 1) & 2))
1786 {
1787 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001788 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001789 log_options(mess->sname, mess->xid);
1790 }
Simon Kelley28866e92011-02-14 20:19:14 +00001791 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001792 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1793
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001794
1795 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001796
Simon Kelley28866e92011-02-14 20:19:14 +00001797 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001798 {
1799 if (mess->siaddr.s_addr != 0)
1800 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1801
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001802 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1803 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1804
Simon Kelley7622fc02009-06-04 20:32:05 +01001805 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1806 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001807
1808 ret = (size_t)(p - (unsigned char *)mess);
1809
1810 if (ret < MIN_PACKETSZ)
1811 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001812
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001813 return ret;
1814}
1815
1816static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1817{
1818 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1819
1820 if (p + len + 3 >= end)
1821 /* not enough space in options area, try and use overload, if poss */
1822 {
1823 unsigned char *overload;
1824
1825 if (!(overload = find_overload(mess)) &&
1826 (mess->file[0] == 0 || mess->sname[0] == 0))
1827 {
1828 /* attempt to overload fname and sname areas, we've reserved space for the
1829 overflow option previuously. */
1830 overload = p;
1831 *(p++) = OPTION_OVERLOAD;
1832 *(p++) = 1;
1833 }
1834
1835 p = NULL;
1836
1837 /* using filename field ? */
1838 if (overload)
1839 {
1840 if (mess->file[0] == 0)
1841 overload[2] |= 1;
1842
1843 if (overload[2] & 1)
1844 {
1845 p = dhcp_skip_opts(mess->file);
1846 if (p + len + 3 >= mess->file + sizeof(mess->file))
1847 p = NULL;
1848 }
1849
1850 if (!p)
1851 {
1852 /* try to bring sname into play (it may be already) */
1853 if (mess->sname[0] == 0)
1854 overload[2] |= 2;
1855
1856 if (overload[2] & 2)
1857 {
1858 p = dhcp_skip_opts(mess->sname);
1859 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1860 p = NULL;
1861 }
1862 }
1863 }
1864
1865 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001866 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 +00001867 }
1868
1869 if (p)
1870 {
1871 *(p++) = opt;
1872 *(p++) = len;
1873 }
1874
1875 return p;
1876}
1877
1878static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1879{
1880 int i;
1881 unsigned char *p = free_space(mess, end, opt, len);
1882
1883 if (p)
1884 for (i = 0; i < len; i++)
1885 *(p++) = val >> (8 * (len - (i + 1)));
1886}
1887
1888static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1889 char *string, int null_term)
1890{
1891 unsigned char *p;
1892 size_t len = strlen(string);
1893
1894 if (null_term && len != 255)
1895 len++;
1896
1897 if ((p = free_space(mess, end, opt, len)))
1898 memcpy(p, string, len);
1899}
1900
1901/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001902static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001903{
1904 int len = opt->len;
1905
1906 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1907 len++;
1908
1909 if (p && len != 0)
1910 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001911 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001912 {
1913 int j;
1914 struct in_addr *a = (struct in_addr *)opt->val;
1915 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1916 {
1917 /* zero means "self" (but not in vendorclass options.) */
1918 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001919 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001920 else
1921 memcpy(p, a, INADDRSZ);
1922 p += INADDRSZ;
1923 }
1924 }
1925 else
1926 memcpy(p, opt->val, len);
1927 }
1928 return len;
1929}
Simon Kelley7622fc02009-06-04 20:32:05 +01001930
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001931static int in_list(unsigned char *list, int opt)
1932{
1933 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001934
1935 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001936 if (!list)
1937 return 1;
1938
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001939 for (i = 0; list[i] != OPTION_END; i++)
1940 if (opt == list[i])
1941 return 1;
1942
1943 return 0;
1944}
1945
Simon Kelley7de060b2011-08-26 17:24:52 +01001946static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001947{
Simon Kelley7de060b2011-08-26 17:24:52 +01001948 struct dhcp_opt *opts;
1949
1950 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1951 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1952 return opts;
1953
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001954 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001955}
1956
Simon Kelley6b010842007-02-12 20:32:07 +00001957/* mark vendor-encapsulated options which match the client-supplied or
1958 config-supplied vendor class */
1959static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1960{
1961 for (; dopt; dopt = dopt->next)
1962 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001963 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001964 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001965 {
1966 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001967 if (dopt->u.vendor_class)
1968 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001969 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001970 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001971 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001972 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001973 break;
1974 }
1975 }
1976 }
1977}
1978
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001979static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1980 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001981{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001982 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001983 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001984 unsigned char *p;
1985
1986 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001987 for (enc_len = 0, start = opt; opt; opt = opt->next)
1988 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001989 {
1990 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001991 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001992 if (enc_len + new <= 255)
1993 enc_len += new;
1994 else
1995 {
1996 p = free_space(mess, end, encap, enc_len);
1997 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001998 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001999 {
2000 len = do_opt(start, p + 2, NULL, null_term);
2001 *(p++) = start->opt;
2002 *(p++) = len;
2003 p += len;
2004 }
2005 enc_len = new;
2006 start = opt;
2007 }
2008 }
2009
2010 if (enc_len != 0 &&
2011 (p = free_space(mess, end, encap, enc_len + 1)))
2012 {
2013 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002014 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002015 {
2016 len = do_opt(start, p + 2, NULL, null_term);
2017 *(p++) = start->opt;
2018 *(p++) = len;
2019 p += len;
2020 }
2021 *p = OPTION_END;
2022 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002023
2024 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002025}
2026
Simon Kelley7622fc02009-06-04 20:32:05 +01002027static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002028{
Simon Kelley7622fc02009-06-04 20:32:05 +01002029 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002030
Simon Kelley7622fc02009-06-04 20:32:05 +01002031 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2032 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2033 memcpy(p, uuid, 17);
2034}
2035
2036static int prune_vendor_opts(struct dhcp_netid *netid)
2037{
2038 int force = 0;
2039 struct dhcp_opt *opt;
2040
2041 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2042 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2043 if (opt->flags & DHOPT_VENDOR_MATCH)
2044 {
2045 if (!match_netid(opt->netid, netid, 1))
2046 opt->flags &= ~DHOPT_VENDOR_MATCH;
2047 else if (opt->flags & DHOPT_FORCE)
2048 force = 1;
2049 }
2050 return force;
2051}
2052
Simon Kelley751d6f42012-02-10 15:24:51 +00002053static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002054{
2055#define NUM_OPTS 4
2056
2057 unsigned char *p, *q;
2058 struct pxe_service *service;
2059 static struct dhcp_opt *o, *ret;
2060 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002061 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002062
2063 /* We pass back references to these, hence they are declared static */
2064 static unsigned char discovery_control;
2065 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2066 static struct dhcp_opt *fake_opts = NULL;
2067
Simon Kelley316e2732010-01-22 20:16:09 +00002068 /* Disable multicast, since we don't support it, and broadcast
2069 unless we need it */
2070 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002071
2072 ret = daemon->dhcp_opts;
2073
2074 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2075 return ret;
2076
2077 for (i = 0; i < NUM_OPTS; i++)
2078 {
2079 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2080 fake_opts[i].netid = NULL;
2081 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2082 }
2083
2084 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2085 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002086 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002087
2088 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2089 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2090 {
2091 size_t len = strlen(service->menu);
2092 /* opt 43 max size is 255. encapsulated option has type and length
2093 bytes, so its max size is 253. */
2094 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2095 {
2096 *(p++) = service->type >> 8;
2097 *(p++) = service->type;
2098 *(p++) = len;
2099 memcpy(p, service->menu, len);
2100 p += len;
2101 i++;
2102 }
2103 else
2104 {
2105 toobig:
2106 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2107 return daemon->dhcp_opts;
2108 }
2109
Simon Kelley751d6f42012-02-10 15:24:51 +00002110 boot_server = service->basename ? local :
2111 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2112
Simon Kelley316e2732010-01-22 20:16:09 +00002113 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002114 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002115 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002116 goto toobig;
2117
2118 /* Boot service with known address - give it */
2119 *(q++) = service->type >> 8;
2120 *(q++) = service->type;
2121 *(q++) = 1;
2122 /* dest misaligned */
2123 memcpy(q, &boot_server.s_addr, INADDRSZ);
2124 q += INADDRSZ;
2125 }
2126 else if (service->type != 0)
2127 /* We don't know the server for a service type, so we'll
2128 allow the client to broadcast for it */
2129 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002130 }
2131
2132 /* if no prompt, wait forever if there's a choice */
2133 fake_prompt[0] = (i > 1) ? 255 : 0;
2134
2135 if (i == 0)
2136 discovery_control = 8; /* no menu - just use use mess->filename */
2137 else
2138 {
2139 ret = &fake_opts[j--];
2140 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2141 ret->val = (unsigned char *)daemon->dhcp_buff;
2142 ret->opt = SUBOPT_PXE_MENU;
2143
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002144 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002145 {
2146 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002147 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2148 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002149 ret->opt = SUBOPT_PXE_SERVERS;
2150 }
2151 }
2152
2153 for (o = daemon->dhcp_opts; o; o = o->next)
2154 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2155 break;
2156
2157 if (!o)
2158 {
2159 ret = &fake_opts[j--];
2160 ret->len = sizeof(fake_prompt);
2161 ret->val = fake_prompt;
2162 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2163 }
2164
Simon Kelley316e2732010-01-22 20:16:09 +00002165 ret = &fake_opts[j--];
2166 ret->len = 1;
2167 ret->opt = SUBOPT_PXE_DISCOVERY;
2168 ret->val= &discovery_control;
2169
Simon Kelley7622fc02009-06-04 20:32:05 +01002170 return ret;
2171}
2172
2173static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2174{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002175 memset(mess->sname, 0, sizeof(mess->sname));
2176 memset(mess->file, 0, sizeof(mess->file));
2177 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2178 mess->siaddr.s_addr = 0;
2179}
Simon Kelleycdeda282006-03-16 20:16:06 +00002180
Simon Kelley7622fc02009-06-04 20:32:05 +01002181struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002182{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002183 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002184
2185 /* decide which dhcp-boot option we're using */
2186 for (boot = daemon->boot_config; boot; boot = boot->next)
2187 if (match_netid(boot->netid, netid, 0))
2188 break;
2189 if (!boot)
2190 /* No match, look for one without a netid */
2191 for (boot = daemon->boot_config; boot; boot = boot->next)
2192 if (match_netid(boot->netid, netid, 1))
2193 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002194
2195 return boot;
2196}
2197
2198static void do_options(struct dhcp_context *context,
2199 struct dhcp_packet *mess,
2200 unsigned char *end,
2201 unsigned char *req_options,
2202 char *hostname,
2203 char *domain, char *config_domain,
2204 struct dhcp_netid *netid,
2205 struct in_addr subnet_addr,
2206 unsigned char fqdn_flags,
2207 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002208 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002209 int vendor_class_len,
2210 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002211{
2212 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2213 struct dhcp_boot *boot;
2214 unsigned char *p;
2215 int i, len, force_encap = 0;
2216 unsigned char f0 = 0, s0 = 0;
2217 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002218 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002219 struct dhcp_netid *tagif;
2220 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002221
Simon Kelley7de060b2011-08-26 17:24:52 +01002222 /* flag options which are valid with the current tag set (sans context tags) */
2223 tagif = run_tag_if(netid);
2224 for (opt = config_opts; opt; opt = opt->next)
2225 {
2226 opt->flags &= ~DHOPT_TAGOK;
2227 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
2228 match_netid(opt->netid, tagif, 0))
2229 opt->flags |= DHOPT_TAGOK;
2230 }
2231
2232 /* now flag options which are valid, including the context tags,
2233 otherwise valid options are inhibited if we found a higher priotity one above */
2234 if (context && context->netid.net)
2235 {
2236 context->netid.next = netid;
2237 tagif = run_tag_if(&context->netid);
2238
2239 for (opt = config_opts; opt; opt = opt->next)
2240 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
2241 match_netid(opt->netid, tagif, 0))
2242 {
2243 struct dhcp_opt *tmp;
2244 for (tmp = config_opts; tmp; tmp = tmp->next)
2245 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
2246 break;
2247 if (!tmp)
2248 opt->flags |= DHOPT_TAGOK;
2249 }
2250 }
2251
2252 /* now flag untagged options which are not overridden by tagged ones */
2253 for (opt = config_opts; opt; opt = opt->next)
2254 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
2255 {
2256 struct dhcp_opt *tmp;
2257 for (tmp = config_opts; tmp; tmp = tmp->next)
2258 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
2259 break;
2260 if (!tmp)
2261 opt->flags |= DHOPT_TAGOK;
2262 else if (!tmp->netid)
2263 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
2264 }
2265
Simon Kelley7622fc02009-06-04 20:32:05 +01002266 if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
2267 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
Simon Kelley1ab84e22004-01-29 16:48:35 +00002268
Simon Kelley7622fc02009-06-04 20:32:05 +01002269 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002270 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002271 {
2272 char *q = daemon->namebuff;
2273 for (i = 0; req_options[i] != OPTION_END; i++)
2274 {
2275 char *s = option_string(req_options[i], NULL, NULL);
2276 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2277 "%d%s%s%s",
2278 req_options[i],
2279 s ? ":" : "",
2280 s ? s : "",
2281 req_options[i+1] == OPTION_END ? "" : ", ");
2282 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2283 {
2284 q = daemon->namebuff;
2285 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2286 }
2287 }
2288 }
2289
Simon Kelley7de060b2011-08-26 17:24:52 +01002290 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2291 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2292 break;
2293 if (id_list)
2294 mess->flags |= htons(0x8000); /* force broadcast */
2295
Simon Kelley73a08a22009-02-05 20:28:08 +00002296 if (context)
2297 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002298
2299 /* See if we can send the boot stuff as options.
2300 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002301 and very old DHCP clients won't have this, we also
2302 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002303 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002304 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002305 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002306 {
2307 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002308 {
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_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002312 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2313 else
Simon Kelley824af852008-02-12 20:43:05 +00002314 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002315 }
2316
2317 if (boot->file)
2318 {
Simon Kelley28866e92011-02-14 20:19:14 +00002319 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002320 req_options &&
2321 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002322 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2323 else
Simon Kelley824af852008-02-12 20:43:05 +00002324 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002325 }
2326
Simon Kelley7de060b2011-08-26 17:24:52 +01002327 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002328 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002329 else if (boot->tftp_sname)
2330 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002331 }
Simon Kelley824af852008-02-12 20:43:05 +00002332 else
2333 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002334 they're not explicitly asked for as options. OPTION_END is used
2335 as an internal way to specify siaddr without using dhcp-boot, for use in
2336 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002337 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002338 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002339 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002340 {
2341 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2342 done_file = 1;
2343 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002344
Simon Kelley824af852008-02-12 20:43:05 +00002345 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002346 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002347 {
2348 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2349 done_server = 1;
2350 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002351
Simon Kelley7de060b2011-08-26 17:24:52 +01002352 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002353 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002354 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002355
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002356 /* We don't want to do option-overload for BOOTP, so make the file and sname
2357 fields look like they are in use, even when they aren't. This gets restored
2358 at the end of this function. */
2359
Simon Kelley28866e92011-02-14 20:19:14 +00002360 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002361 {
2362 f0 = mess->file[0];
2363 mess->file[0] = 1;
2364 s0 = mess->sname[0];
2365 mess->sname[0] = 1;
2366 }
2367
2368 /* At this point, if mess->sname or mess->file are zeroed, they are available
2369 for option overload, reserve space for the overload option. */
2370 if (mess->file[0] == 0 || mess->sname[0] == 0)
2371 end -= 3;
2372
Simon Kelley3be34542004-09-11 19:12:13 +01002373 /* rfc3011 says this doesn't need to be in the requested options list. */
2374 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002375 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002376
2377 /* replies to DHCPINFORM may not have a valid context */
2378 if (context)
2379 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002380 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002381 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2382
2383 /* May not have a "guessed" broadcast address if we got no packets via a relay
2384 from this net yet (ie just unicast renewals after a restart */
2385 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002386 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002387 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2388
2389 /* Same comments as broadcast apply, and also may not be able to get a sensible
2390 default when using subnet select. User must configure by steam in that case. */
2391 if (context->router.s_addr &&
2392 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002393 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002394 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2395
2396 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002397 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002398 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2399 }
Simon Kelley3be34542004-09-11 19:12:13 +01002400
Simon Kelley9009d742008-11-14 20:04:27 +00002401 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002402 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002403 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002404
Simon Kelley824af852008-02-12 20:43:05 +00002405 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002406 if (hostname)
2407 {
Simon Kelley824af852008-02-12 20:43:05 +00002408 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002409 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002410 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002411
2412 if (fqdn_flags != 0)
2413 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002414 len = strlen(hostname) + 3;
2415
Simon Kelley3d8df262005-08-29 12:19:27 +01002416 if (fqdn_flags & 0x04)
2417 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002418 else if (null_term)
2419 len++;
2420
Simon Kelley9009d742008-11-14 20:04:27 +00002421 if (domain)
2422 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002423
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002424 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002425 {
Simon Kelley3d8df262005-08-29 12:19:27 +01002426 *(p++) = fqdn_flags;
2427 *(p++) = 255;
2428 *(p++) = 255;
2429
2430 if (fqdn_flags & 0x04)
2431 {
2432 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002433 if (domain)
2434 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002435 *p++ = 0;
2436 }
2437 else
2438 {
2439 memcpy(p, hostname, strlen(hostname));
2440 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002441 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002442 {
2443 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002444 memcpy(p, domain, strlen(domain));
2445 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002446 }
2447 if (null_term)
2448 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002449 }
2450 }
2451 }
2452 }
2453
Simon Kelley6b010842007-02-12 20:32:07 +00002454 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002455 {
Simon Kelley824af852008-02-12 20:43:05 +00002456 int optno = opt->opt;
2457
Simon Kelley7de060b2011-08-26 17:24:52 +01002458 /* netids match and not encapsulated? */
2459 if (!(opt->flags & DHOPT_TAGOK))
2460 continue;
2461
Simon Kelley6b010842007-02-12 20:32:07 +00002462 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002463 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002464 continue;
2465
2466 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002467 if (optno == OPTION_CLIENT_FQDN ||
2468 optno == OPTION_MAXMESSAGE ||
2469 optno == OPTION_OVERLOAD ||
2470 optno == OPTION_PAD ||
2471 optno == OPTION_END)
2472 continue;
2473
2474 if (optno == OPTION_SNAME && done_server)
2475 continue;
2476
2477 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002478 continue;
2479
Simon Kelley33820b72004-04-03 21:10:00 +01002480 /* For the options we have default values on
2481 dhc-option=<optionno> means "don't include this option"
2482 not "include a zero-length option" */
2483 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002484 (optno == OPTION_NETMASK ||
2485 optno == OPTION_BROADCAST ||
2486 optno == OPTION_ROUTER ||
2487 optno == OPTION_DNSSERVER ||
2488 optno == OPTION_DOMAINNAME ||
2489 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002490 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002491
2492 /* vendor-class comes from elsewhere for PXE */
2493 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2494 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002495
Simon Kelley7622fc02009-06-04 20:32:05 +01002496 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002497 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002498 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002499
Simon Kelley824af852008-02-12 20:43:05 +00002500 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002501 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002502 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002503 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2504
Simon Kelley6b010842007-02-12 20:32:07 +00002505 /* If we send a vendor-id, revisit which vendor-ops we consider
2506 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002507 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002508 {
2509 match_vendor_opts(p - 2, config_opts);
2510 done_vendor_class = 1;
2511 }
Simon Kelley6b010842007-02-12 20:32:07 +00002512 }
2513 }
2514
Simon Kelley73a08a22009-02-05 20:28:08 +00002515 /* Now send options to be encapsulated in arbitrary options,
2516 eg dhcp-option=encap:172,17,.......
Simon Kelley316e2732010-01-22 20:16:09 +00002517 Also hand vendor-identifying vendor-encapsulated options,
2518 dhcp-option = rfc3925-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002519 The may be more that one "outer" to do, so group
2520 all the options which match each outer in turn. */
2521 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002522 opt->flags &= ~DHOPT_ENCAP_DONE;
2523
2524 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002525 {
2526 int flags;
2527
2528 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2529 {
2530 int found = 0;
2531 struct dhcp_opt *o;
2532
2533 if (opt->flags & DHOPT_ENCAP_DONE)
2534 continue;
2535
2536 for (len = 0, o = config_opts; o; o = o->next)
2537 {
2538 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2539
2540 o->flags &= ~DHOPT_ENCAP_MATCH;
2541
2542 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2543 continue;
2544
2545 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002546 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002547 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2548 {
2549 o->flags |= DHOPT_ENCAP_MATCH;
2550 found = 1;
2551 len += do_opt(o, NULL, NULL, 0) + 2;
2552 }
2553 }
2554
2555 if (found)
2556 {
2557 if (flags & DHOPT_ENCAPSULATE)
2558 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2559 else if (len > 250)
2560 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2561 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2562 {
2563 int swap_ent = htonl(opt->u.encap);
2564 memcpy(p, &swap_ent, 4);
2565 p += 4;
2566 *(p++) = len;
2567 for (o = config_opts; o; o = o->next)
2568 if (o->flags & DHOPT_ENCAP_MATCH)
2569 {
2570 len = do_opt(o, p + 2, NULL, 0);
2571 *(p++) = o->opt;
2572 *(p++) = len;
2573 p += len;
2574 }
2575 }
2576 }
2577 }
2578 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002579
Simon Kelley7de060b2011-08-26 17:24:52 +01002580 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002581
Simon Kelley316e2732010-01-22 20:16:09 +00002582 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002583 {
2584 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002585 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002586 }
2587
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002588 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2589 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2590 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2591 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2592 /* If we send vendor encapsulated options, and haven't already sent option 60,
2593 echo back the value we got from the client. */
2594 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2595
Simon Kelley7622fc02009-06-04 20:32:05 +01002596 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002597 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002598 {
2599 mess->file[0] = f0;
2600 mess->sname[0] = s0;
2601 }
2602}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002603
Simon Kelley7622fc02009-06-04 20:32:05 +01002604#endif
2605
2606
2607
2608
2609
2610
2611