blob: 0e075f9ec51b8a704650aac4ef1247a6d11ca487 [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 Kelley316e2732010-01-22 20:16:09 +000066static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local);
Simon Kelley7622fc02009-06-04 20:32:05 +010067struct dhcp_boot *find_boot(struct dhcp_netid *netid);
68
69
Simon Kelley824af852008-02-12 20:43:05 +000070size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley7de060b2011-08-26 17:24:52 +010071 size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
Simon Kelley33820b72004-04-03 21:10:00 +010072{
Simon Kelley26128d22004-11-14 16:43:54 +000073 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000074 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010075 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000076 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000077 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +010078 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000079 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000080 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010081 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000082 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000083 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010084 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000085 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000086 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000087 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010088 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010089 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010090 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010091 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010092 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010093 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000094 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010095 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000096 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000097 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010098 unsigned char pxe_uuid[17];
Simon Kelley316e2732010-01-22 20:16:09 +000099 unsigned char *oui = NULL, *serial = NULL, *class = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +0100100
Simon Kelley1a6bca82008-07-11 11:11:42 +0100101 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000102
103 /* set tag with name == interface */
104 iface_id.net = iface_name;
105 iface_id.next = NULL;
106 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100107
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100108 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100109 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000110
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100111 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000112 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100113
Simon Kelley3be34542004-09-11 19:12:13 +0100114 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000115 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000116 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100117 u32 cookie = htonl(DHCP_COOKIE);
118
Simon Kelley3be34542004-09-11 19:12:13 +0100119 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100120 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100121 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100122
123 mess_type = option_uint(opt, 0, 1);
124
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100125 /* two things to note here: expand_buf may move the packet,
126 so reassign mess from daemon->packet. Also, the size
127 sent includes the IP and UDP headers, hence the magic "-28" */
128 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
129 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100130 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100131
132 if (size > DHCP_PACKET_MAX)
133 size = DHCP_PACKET_MAX;
134 else if (size < sizeof(struct dhcp_packet))
135 size = sizeof(struct dhcp_packet);
136
137 if (expand_buf(&daemon->dhcp_packet, size))
138 {
Simon Kelley824af852008-02-12 20:43:05 +0000139 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100140 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100141 }
142 }
143
Simon Kelley3be34542004-09-11 19:12:13 +0100144 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
145 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000146 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100147 mess->ciaddr.s_addr = 0;
148
Simon Kelley316e2732010-01-22 20:16:09 +0000149 /* search for device identity from CPEWAN devices, we pass this through to the script */
150 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
151 {
152 unsigned int elen, offset, len = option_len(opt);
153
154 for (offset = 0; offset < (len - 5); offset += elen + 5)
155 {
156 elen = option_uint(opt, offset + 4 , 1);
157 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
158 {
159 unsigned char *x = option_ptr(opt, offset + 5);
160 unsigned char *y = option_ptr(opt, offset + elen + 5);
161 oui = option_find1(x, y, 1, 1);
162 serial = option_find1(x, y, 2, 1);
163 class = option_find1(x, y, 3, 1);
164
165 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
166 the gateway id back. Note that the device class is optional */
167 if (oui && serial)
168 {
169 cpewan_id.net = "cpewan-id";
170 cpewan_id.next = netid;
171 netid = &cpewan_id;
172 }
173 break;
174 }
175 }
176 }
177
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100178 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
179 {
180 /* Any agent-id needs to be copied back out, verbatim, as the last option
181 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
182 get overwritten, then it will be shuffled back at the end of processing.
183 Note that the incoming options must not be overwritten here, so there has to
184 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100185 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100186 unsigned int total = option_len(opt) + 2;
187 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
188 if (last_opt && last_opt < end - total)
189 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100190 end -= total;
191 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100192 memcpy(agent_id, opt, total);
193 }
194
195 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100196 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100197 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100198
199 /* look for RFC5107 server-identifier-override */
200 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
201 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100202
203 /* if a circuit-id or remote-is option is provided, exact-match to options. */
204 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
205 {
206 int search;
207
208 if (vendor->match_type == MATCH_CIRCUIT)
209 search = SUBOPT_CIRCUIT_ID;
210 else if (vendor->match_type == MATCH_REMOTE)
211 search = SUBOPT_REMOTE_ID;
212 else if (vendor->match_type == MATCH_SUBSCRIBER)
213 search = SUBOPT_SUBSCR_ID;
214 else
215 continue;
216
Simon Kelley1a6bca82008-07-11 11:11:42 +0100217 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100218 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100219 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100220 {
221 vendor->netid.next = netid;
222 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100223 }
224 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100225 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100226
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100227 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
228 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100229 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000230
231 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000232 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000233 {
Simon Kelley26128d22004-11-14 16:43:54 +0000234 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100235 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000236 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000237
Simon Kelley0a852542005-03-23 20:28:59 +0000238 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100239 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000240
241 /* If this request is missing a clid, but we've seen one before,
242 use it again for option matching etc. */
243 if (lease && !clid && lease->clid)
244 {
245 clid_len = lease->clid_len;
246 clid = lease->clid;
247 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000248
249 /* find mac to use for logging and hashing */
250 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000251 }
Simon Kelley3be34542004-09-11 19:12:13 +0100252
Simon Kelleycdeda282006-03-16 20:16:06 +0000253 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
254 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100255 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
256 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000257 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100258 mac->netid.next = netid;
259 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000260 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100261
Simon Kelley0a852542005-03-23 20:28:59 +0000262 /* Determine network for this packet. Our caller will have already linked all the
263 contexts which match the addresses of the receiving interface but if the
264 machine has an address already, or came via a relay, or we have a subnet selector,
265 we search again. If we don't have have a giaddr or explicit subnet selector,
266 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100267 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
268 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000269 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
270 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100271 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100272 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100273 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000274
Simon Kelley3d8df262005-08-29 12:19:27 +0100275 if (subnet_addr.s_addr)
276 {
277 addr = subnet_addr;
278 force = 1;
279 }
280 else if (mess->giaddr.s_addr)
281 {
282 addr = mess->giaddr;
283 force = 1;
284 }
Simon Kelley16972692006-10-16 20:04:18 +0100285 else
286 {
287 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
288 addr = mess->ciaddr;
289 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
290 if (context_tmp->netmask.s_addr &&
291 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
292 is_same_net(addr, context_tmp->end, context_tmp->netmask))
293 {
294 context_new = context;
295 break;
296 }
297 }
298
299 if (!context_new)
300 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100301 {
302 struct in_addr netmask = context_tmp->netmask;
303
304 /* guess the netmask for relayed networks */
305 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
306 {
307 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
308 netmask.s_addr = htonl(0xff000000);
309 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
310 netmask.s_addr = htonl(0xffff0000);
311 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
312 netmask.s_addr = htonl(0xffffff00);
313 }
314
315 /* This section fills in context mainly when a client which is on a remote (relayed)
316 network renews a lease without using the relay, after dnsmasq has restarted. */
317 if (netmask.s_addr != 0 &&
318 is_same_net(addr, context_tmp->start, netmask) &&
319 is_same_net(addr, context_tmp->end, netmask))
320 {
321 context_tmp->netmask = netmask;
322 if (context_tmp->local.s_addr == 0)
323 context_tmp->local = fallback;
324 if (context_tmp->router.s_addr == 0)
325 context_tmp->router = mess->giaddr;
326
327 /* fill in missing broadcast addresses for relayed ranges */
328 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
329 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
330
331 context_tmp->current = context_new;
332 context_new = context_tmp;
333 }
334 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100335
Simon Kelley3d8df262005-08-29 12:19:27 +0100336 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100337 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000338 }
Simon Kelley3be34542004-09-11 19:12:13 +0100339
340 if (!context)
341 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100342 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100343 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
344 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
Simon Kelley3be34542004-09-11 19:12:13 +0100345 return 0;
346 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100347
Simon Kelley28866e92011-02-14 20:19:14 +0000348 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100349 {
350 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100351 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
352 {
353 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100354 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100355 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100356 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100357 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100358 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100359 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100360 }
361 }
362
Simon Kelley3be34542004-09-11 19:12:13 +0100363 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100364
Simon Kelleycdeda282006-03-16 20:16:06 +0000365 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
366 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100367
368 /* set "known" tag for known hosts */
369 if (config)
370 {
371 known_id.net = "known";
372 known_id.next = netid;
373 netid = &known_id;
374 }
Simon Kelley26128d22004-11-14 16:43:54 +0000375
Simon Kelley316e2732010-01-22 20:16:09 +0000376 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100377 {
378 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000379 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000380 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100381
382 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100383 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100384 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000385
Simon Kelley26128d22004-11-14 16:43:54 +0000386 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000387 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000388
389 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
390
391 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000392 {
393 hostname = config->hostname;
394 domain = config->domain;
395 }
396
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100397 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000398 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100399 struct dhcp_netid_list *list;
400
401 for (list = config->netid; list; list = list->next)
402 {
403 list->list->next = netid;
404 netid = list->list;
405 }
Simon Kelley26128d22004-11-14 16:43:54 +0000406 }
407
408 /* Match incoming filename field as a netid. */
409 if (mess->file[0])
410 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000411 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
412 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
413 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000414 id.next = netid;
415 netid = &id;
416 }
Simon Kelley6b010842007-02-12 20:32:07 +0000417
418 /* Add "bootp" as a tag to allow different options, address ranges etc
419 for BOOTP clients */
420 bootp_id.net = "bootp";
421 bootp_id.next = netid;
422 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000423
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100424 tagif_netid = run_tag_if(netid);
425
Simon Kelley26128d22004-11-14 16:43:54 +0000426 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100427 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100428 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000429
Simon Kelley3d8df262005-08-29 12:19:27 +0100430 if (!message)
431 {
Simon Kelley9009d742008-11-14 20:04:27 +0000432 int nailed = 0;
433
Simon Kelley3d8df262005-08-29 12:19:27 +0100434 if (have_config(config, CONFIG_ADDR))
435 {
Simon Kelley9009d742008-11-14 20:04:27 +0000436 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100437 logaddr = &config->addr;
438 mess->yiaddr = config->addr;
439 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000440 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100441 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000442 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000443 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100444 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100445 else
446 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000447 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100448 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000449 {
450 if (lease)
451 {
452 /* lease exists, wrong network. */
453 lease_prune(lease, now);
454 lease = NULL;
455 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100456 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000457 message = _("no address available");
458 }
459 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100460 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100461 }
462
Simon Kelley9009d742008-11-14 20:04:27 +0000463 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
464 message = _("wrong network");
465 else if (context->netid.net)
466 {
467 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100468 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100469 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100470
471 log_tags(tagif_netid, mess);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100472
Simon Kelley9009d742008-11-14 20:04:27 +0000473 if (!message && !nailed)
474 {
475 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100476 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000477 break;
478 if (!id_list)
479 message = _("no address configured");
480 }
481
Simon Kelley7cebd202006-05-06 14:13:33 +0100482 if (!message &&
483 !lease &&
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;
802 if (service->server.s_addr != 0)
803 mess->siaddr = service->server;
804 else
805 mess->siaddr = context->local;
806
807 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
808 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
809 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
810 pxe_misc(mess, end, uuid);
811
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100812 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100813 opt71.val = save71;
814 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
815 opt71.len = 4;
816 opt71.flags = DHOPT_VENDOR_MATCH;
817 opt71.netid = NULL;
818 opt71.next = daemon->dhcp_opts;
819 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
820
821 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100822 log_tags(tagif_netid, mess);
823 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100824 }
825
826 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
827 {
828 pxearch = option_uint(opt, 0, 2);
829
Simon Kelley316e2732010-01-22 20:16:09 +0000830 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000831 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100832 {
Simon Kelley28866e92011-02-14 20:19:14 +0000833 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100834
Simon Kelley28866e92011-02-14 20:19:14 +0000835 for (tmp = context; tmp; tmp = tmp->current)
836 if ((tmp->flags & CONTEXT_PROXY) &&
837 match_netid(tmp->filter, tagif_netid, 1))
838 break;
839
840 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100841 {
Simon Kelley28866e92011-02-14 20:19:14 +0000842 struct dhcp_boot *boot = find_boot(tagif_netid);
843
844 mess->yiaddr.s_addr = 0;
845 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
846 {
847 mess->ciaddr.s_addr = 0;
848 mess->flags |= htons(0x8000); /* broadcast */
849 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100850
Simon Kelley28866e92011-02-14 20:19:14 +0000851 clear_packet(mess, end);
852
853 /* Provide the bootfile here, for gPXE, and in case we have no menu items
854 and set discovery_control = 8 */
855 if (boot)
856 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100857 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000858 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100859 else if (boot->tftp_sname)
860 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000861
862 if (boot->file)
863 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
864 }
865
866 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
867 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
868 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
869 pxe_misc(mess, end, uuid);
870 prune_vendor_opts(tagif_netid);
871 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
872
873 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley7de060b2011-08-26 17:24:52 +0100874 log_tags(tagif_netid, mess);
875 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100876 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100877 }
878 }
879 }
880
881 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000882 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100883 return 0;
884
Simon Kelleybb01cb92004-12-13 20:56:23 +0000885 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100886 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100887 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100888 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000889 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100890 }
891
Simon Kelley3be34542004-09-11 19:12:13 +0100892 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000893 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000894 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000895 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000896 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000897 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100898
Simon Kelley44a2a312004-03-10 20:04:35 +0000899 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100900 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000901
Simon Kelleybb01cb92004-12-13 20:56:23 +0000902 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000903 return 0;
904
Simon Kelley7622fc02009-06-04 20:32:05 +0100905 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000906
907 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
908 lease_prune(lease, now);
909
Simon Kelley33820b72004-04-03 21:10:00 +0100910 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000911 config->addr.s_addr == option_addr(opt).s_addr)
912 {
Simon Kelley849a8352006-06-09 21:02:31 +0100913 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100914 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100915 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100916 config->flags |= CONFIG_DECLINED;
917 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000918 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100919 else
920 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100921 for (; context; context = context->current)
922 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000923
924 return 0;
925
926 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100927 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100928 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000929 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000930 return 0;
931
Simon Kelley44a2a312004-03-10 20:04:35 +0000932 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
933 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100934 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000935 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100936
Simon Kelley7622fc02009-06-04 20:32:05 +0100937 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000938
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000939 return 0;
940
941 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100942 if (ignore || have_config(config, CONFIG_DISABLE))
943 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000944 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100945 opt = NULL;
946 }
947 else
948 {
949 struct in_addr addr, conf;
950
Simon Kelley1a6bca82008-07-11 11:11:42 +0100951 addr.s_addr = conf.s_addr = 0;
952
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100953 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
954 addr = option_addr(opt);
955
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100956 if (have_config(config, CONFIG_ADDR))
957 {
Simon Kelley849a8352006-06-09 21:02:31 +0100958 char *addrs = inet_ntoa(config->addr);
959
Simon Kelley9009d742008-11-14 20:04:27 +0000960 if ((ltmp = lease_find_by_addr(config->addr)) &&
961 ltmp != lease &&
962 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000963 {
964 int len;
965 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
966 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100967 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100968 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000969 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100970 else
971 {
972 struct dhcp_context *tmp;
973 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100974 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100975 break;
976 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100977 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 +0100978 else if (have_config(config, CONFIG_DECLINED) &&
979 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100980 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100981 else
982 conf = config->addr;
983 }
984 }
985
986 if (conf.s_addr)
987 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100988 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100989 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100990 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100991 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100992 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100993 !config_find_by_address(daemon->dhcp_conf, addr))
994 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100995 else if (emac_len == 0)
996 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100997 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100998 message = _("no address available");
999 }
1000
Simon Kelley7622fc02009-06-04 20:32:05 +01001001 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001002
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001003 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001004 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001005
Simon Kelleycdeda282006-03-16 20:16:06 +00001006 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001007 {
1008 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001009 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001010 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001011
1012 log_tags(tagif_netid, mess);
1013
1014 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1015
Simon Kelley824af852008-02-12 20:43:05 +00001016 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001017 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001018 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001019 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001020 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001021 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001022 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001023 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001024 option_put(mess, end, OPTION_T1, 4, (time/2));
1025 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001026 }
Simon Kelley9009d742008-11-14 20:04:27 +00001027 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001028 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001029
Simon Kelley7de060b2011-08-26 17:24:52 +01001030 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001031
1032 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001033 if (ignore || have_config(config, CONFIG_DISABLE))
1034 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001035 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001036 {
1037 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001038 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001039
Simon Kelley4011c4e2006-10-28 16:26:19 +01001040 /* send vendor and user class info for new or recreated lease */
1041 do_classes = 1;
1042
Simon Kelleybb01cb92004-12-13 20:56:23 +00001043 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001044 {
Simon Kelley3be34542004-09-11 19:12:13 +01001045 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001046 selecting = 1;
1047
Simon Kelley1a6bca82008-07-11 11:11:42 +01001048 if (override.s_addr != 0)
1049 {
1050 if (option_addr(opt).s_addr != override.s_addr)
1051 return 0;
1052 }
Simon Kelley9009d742008-11-14 20:04:27 +00001053 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001054 {
1055 for (; context; context = context->current)
1056 if (context->local.s_addr == option_addr(opt).s_addr)
1057 break;
1058
1059 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001060 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001061 /* Handle very strange configs where clients have more than one route to the server.
1062 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1063 Have to set override to make sure we echo back the correct server-id */
1064 struct irec *intr;
1065
1066 enumerate_interfaces();
1067
1068 for (intr = daemon->interfaces; intr; intr = intr->next)
1069 if (intr->addr.sa.sa_family == AF_INET &&
1070 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1071 intr->tftp_ok)
1072 break;
1073
1074 if (intr)
1075 override = intr->addr.in.sin_addr;
1076 else
1077 {
1078 /* In auth mode, a REQUEST sent to the wrong server
1079 should be faulted, so that the client establishes
1080 communication with us, otherwise, silently ignore. */
1081 if (!option_bool(OPT_AUTHORITATIVE))
1082 return 0;
1083 message = _("wrong server-ID");
1084 }
Simon Kelley9009d742008-11-14 20:04:27 +00001085 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001086 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001087
Simon Kelley3be34542004-09-11 19:12:13 +01001088 /* If a lease exists for this host and another address, squash it. */
1089 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1090 {
1091 lease_prune(lease, now);
1092 lease = NULL;
1093 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001094 }
Simon Kelley3be34542004-09-11 19:12:13 +01001095 else
1096 {
1097 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001098 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001099 return 0;
1100
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001101 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001102 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001103 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001104 }
1105 else
1106 {
1107 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001108 /* Check existing lease for this address.
1109 We allow it to be missing if dhcp-authoritative mode
1110 as long as we can allocate the lease now - checked below.
1111 This makes for a smooth recovery from a lost lease DB */
1112 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001113 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001114 {
Simon Kelley28866e92011-02-14 20:19:14 +00001115 /* A client rebinding will broadcast the request, so we may see it even
1116 if the lease is held by another server. Just ignore it in that case.
1117 If the request is unicast to us, then somethings wrong, NAK */
1118 if (!unicast_dest)
1119 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001120 message = _("lease not found");
1121 /* ensure we broadcast NAK */
1122 unicast_dest = 0;
1123 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001124
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001125 /* desynchronise renewals */
1126 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001127 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001128 }
1129
Simon Kelley7622fc02009-06-04 20:32:05 +01001130 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001131
Simon Kelleydfa666f2004-08-02 18:27:27 +01001132 if (!message)
1133 {
1134 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001135 struct dhcp_context *tmp = NULL;
1136
1137 if (have_config(config, CONFIG_ADDR))
1138 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001139 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001140 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001141
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001142 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001143 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001144 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001145 message = _("wrong network");
1146 /* ensure we broadcast NAK */
1147 unicast_dest = 0;
1148 }
1149
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001150 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001151 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001152 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001153 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001154
Simon Kelleydfa666f2004-08-02 18:27:27 +01001155 /* Check if a new static address has been configured. Be very sure that
1156 when the client does DISCOVER, it will get the static address, otherwise
1157 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001158 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001159 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001160 (!have_config(config, CONFIG_DECLINED) ||
1161 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001162 config->addr.s_addr != mess->yiaddr.s_addr &&
1163 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001164 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001165
1166 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001167 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001168 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001169
Simon Kelley9009d742008-11-14 20:04:27 +00001170 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1171 {
1172 /* If a host is configured with more than one MAC address, it's OK to 'nix
1173 a lease from one of it's MACs to give the address to another. */
1174 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1175 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001176 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001177 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1178 inet_ntoa(ltmp->addr));
1179 lease = ltmp;
1180 }
Simon Kelley16972692006-10-16 20:04:18 +01001181 else
Simon Kelley9009d742008-11-14 20:04:27 +00001182 message = _("address in use");
1183 }
1184
1185 if (!message)
1186 {
1187 if (emac_len == 0)
1188 message = _("no unique-id");
1189
1190 else if (!lease)
1191 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001192 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001193 do_classes = 1;
1194 else
1195 message = _("no leases left");
1196 }
Simon Kelley16972692006-10-16 20:04:18 +01001197 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001198 }
Simon Kelley16972692006-10-16 20:04:18 +01001199
Simon Kelley44a2a312004-03-10 20:04:35 +00001200 if (message)
1201 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001202 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001203
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001204 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001205 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001206 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001207 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001208 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001209 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1210 a distant subnet which unicast a REQ to us won't work. */
1211 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1212 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1213 {
1214 mess->flags |= htons(0x8000); /* broadcast */
1215 mess->ciaddr.s_addr = 0;
1216 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001217 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001218 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001219 {
Simon Kelley316e2732010-01-22 20:16:09 +00001220 if (context->netid.net)
1221 {
1222 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001223 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001224 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001225
1226 log_tags(tagif_netid, mess);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001227
Simon Kelley316e2732010-01-22 20:16:09 +00001228#ifdef HAVE_SCRIPT
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001229 if (do_classes && daemon->lease_change_command)
1230 {
1231 struct dhcp_netid *n;
1232
1233 if (mess->giaddr.s_addr)
1234 lease->giaddr = mess->giaddr;
1235
1236 lease->changed = 1;
1237 free(lease->extradata);
Simon Kelley28866e92011-02-14 20:19:14 +00001238 lease->extradata = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001239 lease->extradata_size = lease->extradata_len = 0;
1240
1241 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1242 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1243 add_extradata_opt(lease, oui);
1244 add_extradata_opt(lease, serial);
1245 add_extradata_opt(lease, class);
1246
1247 /* space-concat tag set */
1248 if (!tagif_netid)
1249 add_extradata_opt(lease, NULL);
1250 else
1251 for (n = tagif_netid; n; n = n->next)
Simon Kelley39bec5f2012-01-06 22:36:58 +00001252 {
1253 struct dhcp_netid *n1;
1254 /* kill dupes */
1255 for (n1 = n->next; n1; n1 = n1->next)
1256 if (strcmp(n->net, n1->net) == 0)
1257 break;
1258 if (!n1)
1259 add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1260 }
1261
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001262 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1263 {
1264 int len = option_len(opt);
1265 unsigned char *ucp = option_ptr(opt, 0);
1266 /* If the user-class option started as counted strings, the first byte will be zero. */
1267 if (len != 0 && ucp[0] == 0)
1268 ucp++, len--;
1269 add_extradata_data(lease, ucp, len, 0);
1270 }
1271 }
Simon Kelley316e2732010-01-22 20:16:09 +00001272#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001273
1274 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1275 {
1276 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001277 hostname = client_hostname;
1278 hostname_auth = 1;
1279 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001280
Simon Kelley824af852008-02-12 20:43:05 +00001281 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +00001282 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001283
Simon Kelley832af0b2007-01-21 20:01:28 +00001284 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1285 if (!hostname_auth)
1286 {
1287 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001288 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001289 break;
1290 if (id_list)
1291 hostname = NULL;
1292 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001293
1294 /* Last ditch, if configured, generate hostname from mac address */
1295 if (!hostname && emac_len != 0)
1296 {
1297 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1298 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1299 break;
1300 if (id_list)
1301 {
1302 int i;
1303
1304 hostname = daemon->dhcp_buff;
1305 /* buffer is 256 bytes, 3 bytes per octet */
1306 for (i = 0; (i < emac_len) && (i < 80); i++)
1307 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1308 hostname = daemon->dhcp_buff;
1309 }
1310 }
1311
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001312 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +00001313 lease_set_hostname(lease, hostname, hostname_auth);
Simon Kelley832af0b2007-01-21 20:01:28 +00001314
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001315 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +00001316 lease_set_interface(lease, int_index);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001317
1318 if (override.s_addr != 0)
1319 lease->override = override;
1320 else
1321 override = lease->override;
1322
Simon Kelley7622fc02009-06-04 20:32:05 +01001323 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001324
Simon Kelley7622fc02009-06-04 20:32:05 +01001325 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001326 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001327 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001328 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001329 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001330 {
Simon Kelley59353a62004-11-21 19:34:28 +00001331 while (fuzz > (time/16))
1332 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001333 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1334 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001335 }
Simon Kelley9009d742008-11-14 20:04:27 +00001336 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001337 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001338 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001339
Simon Kelley7de060b2011-08-26 17:24:52 +01001340 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001341
1342 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001343 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001344 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001345
Simon Kelley7622fc02009-06-04 20:32:05 +01001346 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001347
Simon Kelley73a08a22009-02-05 20:28:08 +00001348 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001349 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001350
1351 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001352 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001353
Simon Kelley5aabfc72007-08-29 11:24:47 +01001354 /* Find a least based on IP address if we didn't
1355 get one from MAC address/client-d */
1356 if (!lease &&
1357 (lease = lease_find_by_addr(mess->ciaddr)) &&
1358 lease->hostname)
1359 hostname = lease->hostname;
1360
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001361 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1362 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001363
Simon Kelley73a08a22009-02-05 20:28:08 +00001364 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001365 {
1366 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001367 tagif_netid = run_tag_if( &context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001368 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001369
1370 log_tags(tagif_netid, mess);
1371
1372 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001373
Simon Kelley3927da42008-07-20 15:10:39 +01001374 if (lease)
1375 {
1376 if (override.s_addr != 0)
1377 lease->override = override;
1378 else
1379 override = lease->override;
1380 }
1381
Simon Kelley7622fc02009-06-04 20:32:05 +01001382 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001383 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001384 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1385
Simon Kelley5aabfc72007-08-29 11:24:47 +01001386 if (lease)
1387 {
1388 if (lease->expires == 0)
1389 time = 0xffffffff;
1390 else
1391 time = (unsigned int)difftime(lease->expires, now);
1392 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001393 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001394 }
Simon Kelley824af852008-02-12 20:43:05 +00001395
Simon Kelley9009d742008-11-14 20:04:27 +00001396 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley7de060b2011-08-26 17:24:52 +01001397 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001398
Simon Kelley5aabfc72007-08-29 11:24:47 +01001399 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001400 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001401 }
1402
1403 return 0;
1404}
1405
Simon Kelley316e2732010-01-22 20:16:09 +00001406static int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
1407{
1408 int i;
1409
1410 if (o->len > len)
1411 return 0;
1412
1413 if (o->len == 0)
1414 return 1;
1415
1416 if (o->flags & DHOPT_HEX)
1417 {
1418 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
1419 return 1;
1420 }
1421 else
1422 for (i = 0; i <= (len - o->len); )
1423 {
1424 if (memcmp(o->val, p + i, o->len) == 0)
1425 return 1;
1426
1427 if (o->flags & DHOPT_STRING)
1428 i++;
1429 else
1430 i += o->len;
1431 }
1432
1433 return 0;
1434}
1435
1436
Simon Kelley6b010842007-02-12 20:32:07 +00001437/* find a good value to use as MAC address for logging and address-allocation hashing.
1438 This is normally just the chaddr field from the DHCP packet,
1439 but eg Firewire will have hlen == 0 and use the client-id instead.
1440 This could be anything, but will normally be EUI64 for Firewire.
1441 We assume that if the first byte of the client-id equals the htype byte
1442 then the client-id is using the usual encoding and use the rest of the
1443 client-id: if not we can use the whole client-id. This should give
1444 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001445unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001446 int clid_len, unsigned char *clid, int *len_out)
1447{
1448 if (hwlen == 0 && clid && clid_len > 3)
1449 {
1450 if (clid[0] == hwtype)
1451 {
1452 *len_out = clid_len - 1 ;
1453 return clid + 1;
1454 }
1455
1456#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1457 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1458 {
1459 *len_out = clid_len - 1 ;
1460 return clid + 1;
1461 }
1462#endif
1463
1464 *len_out = clid_len;
1465 return clid;
1466 }
1467
1468 *len_out = hwlen;
1469 return hwaddr;
1470}
1471
Simon Kelley824af852008-02-12 20:43:05 +00001472static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001473{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001474 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001475
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001476 if (opt)
1477 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001478 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001479 if (req_time < 120 )
1480 req_time = 120; /* sanity */
1481 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1482 time = req_time;
1483 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001484
1485 return time;
1486}
1487
Simon Kelley73a08a22009-02-05 20:28:08 +00001488static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001489{
Simon Kelley73a08a22009-02-05 20:28:08 +00001490 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001491 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001492 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001493 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001494 else
1495 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001496}
1497
Simon Kelleyf2621c72007-04-29 19:47:21 +01001498static int sanitise(unsigned char *opt, char *buf)
1499{
1500 char *p;
1501 int i;
1502
1503 *buf = 0;
1504
1505 if (!opt)
1506 return 0;
1507
Simon Kelley1a6bca82008-07-11 11:11:42 +01001508 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001509
1510 for (i = option_len(opt); i > 0; i--)
1511 {
1512 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001513 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001514 *buf++ = c;
1515 }
1516 *buf = 0; /* add terminator */
1517
1518 return 1;
1519}
1520
Simon Kelley316e2732010-01-22 20:16:09 +00001521#ifdef HAVE_SCRIPT
1522static void add_extradata_data(struct dhcp_lease *lease, unsigned char *data, size_t len, int delim)
1523{
1524 if ((lease->extradata_size - lease->extradata_len) < (len + 1))
1525 {
1526 size_t newsz = lease->extradata_len + len + 100;
1527 unsigned char *new = whine_malloc(newsz);
1528
1529 if (!new)
1530 return;
1531
1532 if (lease->extradata)
1533 {
1534 memcpy(new, lease->extradata, lease->extradata_len);
1535 free(lease->extradata);
1536 }
1537
1538 lease->extradata = new;
1539 lease->extradata_size = newsz;
1540 }
1541
1542 if (len != 0)
1543 memcpy(lease->extradata + lease->extradata_len, data, len);
1544 lease->extradata[lease->extradata_len + len] = delim;
1545 lease->extradata_len += len + 1;
1546}
1547
1548static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1549{
1550 if (!opt)
1551 add_extradata_data(lease, NULL, 0, 0);
1552 else
1553 {
1554 size_t i, len = option_len(opt);
1555 unsigned char *ucp = option_ptr(opt, 0);
1556
1557 /* check for embeded NULLs */
1558 for (i = 0; i < len; i++)
1559 if (ucp[i] == 0)
1560 {
1561 len = i;
1562 break;
1563 }
1564
1565 add_extradata_data(lease, ucp, len, 0);
1566 }
1567}
1568#endif
1569
Simon Kelley5aabfc72007-08-29 11:24:47 +01001570static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001571 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001572{
Simon Kelley16972692006-10-16 20:04:18 +01001573 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001574
Simon Kelley16972692006-10-16 20:04:18 +01001575 /* addr may be misaligned */
1576 if (addr)
1577 memcpy(&a, addr, sizeof(a));
1578
Simon Kelley7622fc02009-06-04 20:32:05 +01001579 print_mac(daemon->namebuff, ext_mac, mac_len);
1580
Simon Kelley28866e92011-02-14 20:19:14 +00001581 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001582 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1583 ntohl(xid),
1584 type,
1585 interface,
1586 addr ? inet_ntoa(a) : "",
1587 addr ? " " : "",
1588 daemon->namebuff,
1589 string ? string : "");
1590 else
1591 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1592 type,
1593 interface,
1594 addr ? inet_ntoa(a) : "",
1595 addr ? " " : "",
1596 daemon->namebuff,
1597 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001598}
1599
Simon Kelley7622fc02009-06-04 20:32:05 +01001600static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001601{
1602 while (*start != OPTION_END)
1603 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001604 int is_ip, is_name, i;
1605 char *text = option_string(start[0], &is_ip, &is_name);
1606 unsigned char trunc = option_len(start);
1607
1608 if (is_ip)
1609 for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ)
1610 {
1611 if (i != 0)
1612 strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
1613 strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
1614 }
1615 else if (!is_name || !sanitise(start, daemon->namebuff))
1616 {
1617 if (trunc > 13)
1618 trunc = 13;
1619 print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
1620 }
1621
1622 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s",
1623 ntohl(xid), option_len(start), start[0],
Simon Kelleyf2621c72007-04-29 19:47:21 +01001624 text ? ":" : "", text ? text : "",
Simon Kelley7622fc02009-06-04 20:32:05 +01001625 trunc == 0 ? "" : " ",
1626 trunc == 0 ? "" : daemon->namebuff,
1627 trunc == option_len(start) ? "" : "...");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001628 start += start[1] + 2;
1629 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001630}
1631
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001632static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001633{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001634 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001635 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001636 if (p > end)
1637 return NULL;
1638 else if (*p == OPTION_END)
1639 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001640 else if (*p == OPTION_PAD)
1641 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001642 else
1643 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001644 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001645 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001646 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001647 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001648 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001649 return NULL; /* malformed packet */
1650 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001651 return p;
1652 p += opt_len + 2;
1653 }
1654 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001655}
1656
Simon Kelleycdeda282006-03-16 20:16:06 +00001657static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001658{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001659 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001660
Simon Kelley3be34542004-09-11 19:12:13 +01001661 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001662 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1663 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001664
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001665 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001666 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001667 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001668
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001669 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001670 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001671 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1672 return ret;
1673
1674 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001675 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001676 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1677 return ret;
1678
1679 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001680}
1681
Simon Kelley7622fc02009-06-04 20:32:05 +01001682static struct in_addr option_addr_arr(unsigned char *opt, int offset)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001683{
1684 /* this worries about unaligned data in the option. */
1685 /* struct in_addr is network byte order */
1686 struct in_addr ret;
1687
Simon Kelley7622fc02009-06-04 20:32:05 +01001688 memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001689
1690 return ret;
1691}
1692
Simon Kelley7622fc02009-06-04 20:32:05 +01001693static struct in_addr option_addr(unsigned char *opt)
1694{
1695 return option_addr_arr(opt, 0);
1696}
1697
1698static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001699{
1700 /* this worries about unaligned data and byte order */
1701 unsigned int ret = 0;
1702 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001703 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001704
1705 for (i = 0; i < size; i++)
1706 ret = (ret << 8) | *p++;
1707
1708 return ret;
1709}
1710
1711static unsigned char *dhcp_skip_opts(unsigned char *start)
1712{
1713 while (*start != 0)
1714 start += start[1] + 2;
1715 return start;
1716}
1717
1718/* only for use when building packet: doesn't check for bad data. */
1719static unsigned char *find_overload(struct dhcp_packet *mess)
1720{
1721 unsigned char *p = &mess->options[0] + sizeof(u32);
1722
1723 while (*p != 0)
1724 {
1725 if (*p == OPTION_OVERLOAD)
1726 return p;
1727 p += p[1] + 2;
1728 }
1729 return NULL;
1730}
1731
Simon Kelley7de060b2011-08-26 17:24:52 +01001732static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001733{
Simon Kelley28866e92011-02-14 20:19:14 +00001734 if (netid && option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001735 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001736 char *s = daemon->namebuff;
1737 for (*s = 0; netid; netid = netid->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001738 {
Simon Kelley9009d742008-11-14 20:04:27 +00001739 /* kill dupes. */
Simon Kelley7de060b2011-08-26 17:24:52 +01001740 struct dhcp_netid *n;
1741
Simon Kelley9009d742008-11-14 20:04:27 +00001742 for (n = netid->next; n; n = n->next)
1743 if (strcmp(netid->net, n->net) == 0)
1744 break;
1745
1746 if (!n)
1747 {
Simon Kelley1f15b812009-10-13 17:49:32 +01001748 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001749 if (netid->next)
Simon Kelley1f15b812009-10-13 17:49:32 +01001750 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
Simon Kelley9009d742008-11-14 20:04:27 +00001751 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001752 }
Simon Kelley1f15b812009-10-13 17:49:32 +01001753 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001754 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001755}
1756
1757static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1758{
1759 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1760 unsigned char *overload;
1761 size_t ret;
1762
1763 /* move agent_id back down to the end of the packet */
1764 if (agent_id)
1765 {
1766 memmove(p, agent_id, real_end - agent_id);
1767 p += real_end - agent_id;
1768 memset(p, 0, real_end - p); /* in case of overlap */
1769 }
1770
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001771 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001772 overload = find_overload(mess);
1773
1774 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001775 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001776 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001777 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001778 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001779 }
Simon Kelley28866e92011-02-14 20:19:14 +00001780 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001781 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1782
1783 if (overload && (option_uint(overload, 0, 1) & 2))
1784 {
1785 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001786 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001787 log_options(mess->sname, mess->xid);
1788 }
Simon Kelley28866e92011-02-14 20:19:14 +00001789 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001790 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1791
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001792
1793 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001794
Simon Kelley28866e92011-02-14 20:19:14 +00001795 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001796 {
1797 if (mess->siaddr.s_addr != 0)
1798 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1799
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001800 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1801 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1802
Simon Kelley7622fc02009-06-04 20:32:05 +01001803 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1804 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001805
1806 ret = (size_t)(p - (unsigned char *)mess);
1807
1808 if (ret < MIN_PACKETSZ)
1809 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001810
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001811 return ret;
1812}
1813
1814static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1815{
1816 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1817
1818 if (p + len + 3 >= end)
1819 /* not enough space in options area, try and use overload, if poss */
1820 {
1821 unsigned char *overload;
1822
1823 if (!(overload = find_overload(mess)) &&
1824 (mess->file[0] == 0 || mess->sname[0] == 0))
1825 {
1826 /* attempt to overload fname and sname areas, we've reserved space for the
1827 overflow option previuously. */
1828 overload = p;
1829 *(p++) = OPTION_OVERLOAD;
1830 *(p++) = 1;
1831 }
1832
1833 p = NULL;
1834
1835 /* using filename field ? */
1836 if (overload)
1837 {
1838 if (mess->file[0] == 0)
1839 overload[2] |= 1;
1840
1841 if (overload[2] & 1)
1842 {
1843 p = dhcp_skip_opts(mess->file);
1844 if (p + len + 3 >= mess->file + sizeof(mess->file))
1845 p = NULL;
1846 }
1847
1848 if (!p)
1849 {
1850 /* try to bring sname into play (it may be already) */
1851 if (mess->sname[0] == 0)
1852 overload[2] |= 2;
1853
1854 if (overload[2] & 2)
1855 {
1856 p = dhcp_skip_opts(mess->sname);
1857 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1858 p = NULL;
1859 }
1860 }
1861 }
1862
1863 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001864 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 +00001865 }
1866
1867 if (p)
1868 {
1869 *(p++) = opt;
1870 *(p++) = len;
1871 }
1872
1873 return p;
1874}
1875
1876static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1877{
1878 int i;
1879 unsigned char *p = free_space(mess, end, opt, len);
1880
1881 if (p)
1882 for (i = 0; i < len; i++)
1883 *(p++) = val >> (8 * (len - (i + 1)));
1884}
1885
1886static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1887 char *string, int null_term)
1888{
1889 unsigned char *p;
1890 size_t len = strlen(string);
1891
1892 if (null_term && len != 255)
1893 len++;
1894
1895 if ((p = free_space(mess, end, opt, len)))
1896 memcpy(p, string, len);
1897}
1898
1899/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001900static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001901{
1902 int len = opt->len;
1903
1904 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1905 len++;
1906
1907 if (p && len != 0)
1908 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001909 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001910 {
1911 int j;
1912 struct in_addr *a = (struct in_addr *)opt->val;
1913 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1914 {
1915 /* zero means "self" (but not in vendorclass options.) */
1916 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001917 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001918 else
1919 memcpy(p, a, INADDRSZ);
1920 p += INADDRSZ;
1921 }
1922 }
1923 else
1924 memcpy(p, opt->val, len);
1925 }
1926 return len;
1927}
Simon Kelley7622fc02009-06-04 20:32:05 +01001928
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001929static int in_list(unsigned char *list, int opt)
1930{
1931 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001932
1933 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001934 if (!list)
1935 return 1;
1936
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001937 for (i = 0; list[i] != OPTION_END; i++)
1938 if (opt == list[i])
1939 return 1;
1940
1941 return 0;
1942}
1943
Simon Kelley7de060b2011-08-26 17:24:52 +01001944static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001945{
Simon Kelley7de060b2011-08-26 17:24:52 +01001946 struct dhcp_opt *opts;
1947
1948 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1949 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1950 return opts;
1951
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001952 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001953}
1954
Simon Kelley6b010842007-02-12 20:32:07 +00001955/* mark vendor-encapsulated options which match the client-supplied or
1956 config-supplied vendor class */
1957static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1958{
1959 for (; dopt; dopt = dopt->next)
1960 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001961 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001962 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001963 {
1964 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001965 if (dopt->u.vendor_class)
1966 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001967 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001968 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001969 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001970 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001971 break;
1972 }
1973 }
1974 }
1975}
1976
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001977static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1978 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001979{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001980 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001981 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001982 unsigned char *p;
1983
1984 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001985 for (enc_len = 0, start = opt; opt; opt = opt->next)
1986 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001987 {
1988 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001989 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001990 if (enc_len + new <= 255)
1991 enc_len += new;
1992 else
1993 {
1994 p = free_space(mess, end, encap, enc_len);
1995 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001996 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001997 {
1998 len = do_opt(start, p + 2, NULL, null_term);
1999 *(p++) = start->opt;
2000 *(p++) = len;
2001 p += len;
2002 }
2003 enc_len = new;
2004 start = opt;
2005 }
2006 }
2007
2008 if (enc_len != 0 &&
2009 (p = free_space(mess, end, encap, enc_len + 1)))
2010 {
2011 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002012 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00002013 {
2014 len = do_opt(start, p + 2, NULL, null_term);
2015 *(p++) = start->opt;
2016 *(p++) = len;
2017 p += len;
2018 }
2019 *p = OPTION_END;
2020 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002021
2022 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00002023}
2024
Simon Kelley7622fc02009-06-04 20:32:05 +01002025static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01002026{
Simon Kelley7622fc02009-06-04 20:32:05 +01002027 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01002028
Simon Kelley7622fc02009-06-04 20:32:05 +01002029 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
2030 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
2031 memcpy(p, uuid, 17);
2032}
2033
2034static int prune_vendor_opts(struct dhcp_netid *netid)
2035{
2036 int force = 0;
2037 struct dhcp_opt *opt;
2038
2039 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
2040 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
2041 if (opt->flags & DHOPT_VENDOR_MATCH)
2042 {
2043 if (!match_netid(opt->netid, netid, 1))
2044 opt->flags &= ~DHOPT_VENDOR_MATCH;
2045 else if (opt->flags & DHOPT_FORCE)
2046 force = 1;
2047 }
2048 return force;
2049}
2050
Simon Kelley316e2732010-01-22 20:16:09 +00002051static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local)
Simon Kelley7622fc02009-06-04 20:32:05 +01002052{
2053#define NUM_OPTS 4
2054
2055 unsigned char *p, *q;
2056 struct pxe_service *service;
2057 static struct dhcp_opt *o, *ret;
2058 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00002059 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01002060
2061 /* We pass back references to these, hence they are declared static */
2062 static unsigned char discovery_control;
2063 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
2064 static struct dhcp_opt *fake_opts = NULL;
2065
Simon Kelley316e2732010-01-22 20:16:09 +00002066 /* Disable multicast, since we don't support it, and broadcast
2067 unless we need it */
2068 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002069
2070 ret = daemon->dhcp_opts;
2071
2072 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
2073 return ret;
2074
2075 for (i = 0; i < NUM_OPTS; i++)
2076 {
2077 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
2078 fake_opts[i].netid = NULL;
2079 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
2080 }
2081
2082 /* create the data for the PXE_MENU and PXE_SERVERS options. */
2083 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002084 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002085
2086 for (i = 0, service = daemon->pxe_services; service; service = service->next)
2087 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
2088 {
2089 size_t len = strlen(service->menu);
2090 /* opt 43 max size is 255. encapsulated option has type and length
2091 bytes, so its max size is 253. */
2092 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
2093 {
2094 *(p++) = service->type >> 8;
2095 *(p++) = service->type;
2096 *(p++) = len;
2097 memcpy(p, service->menu, len);
2098 p += len;
2099 i++;
2100 }
2101 else
2102 {
2103 toobig:
2104 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
2105 return daemon->dhcp_opts;
2106 }
2107
Simon Kelley316e2732010-01-22 20:16:09 +00002108 boot_server = service->basename ? local : service->server;
2109
2110 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002111 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002112 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002113 goto toobig;
2114
2115 /* Boot service with known address - give it */
2116 *(q++) = service->type >> 8;
2117 *(q++) = service->type;
2118 *(q++) = 1;
2119 /* dest misaligned */
2120 memcpy(q, &boot_server.s_addr, INADDRSZ);
2121 q += INADDRSZ;
2122 }
2123 else if (service->type != 0)
2124 /* We don't know the server for a service type, so we'll
2125 allow the client to broadcast for it */
2126 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002127 }
2128
2129 /* if no prompt, wait forever if there's a choice */
2130 fake_prompt[0] = (i > 1) ? 255 : 0;
2131
2132 if (i == 0)
2133 discovery_control = 8; /* no menu - just use use mess->filename */
2134 else
2135 {
2136 ret = &fake_opts[j--];
2137 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2138 ret->val = (unsigned char *)daemon->dhcp_buff;
2139 ret->opt = SUBOPT_PXE_MENU;
2140
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002141 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002142 {
2143 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002144 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2145 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002146 ret->opt = SUBOPT_PXE_SERVERS;
2147 }
2148 }
2149
2150 for (o = daemon->dhcp_opts; o; o = o->next)
2151 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2152 break;
2153
2154 if (!o)
2155 {
2156 ret = &fake_opts[j--];
2157 ret->len = sizeof(fake_prompt);
2158 ret->val = fake_prompt;
2159 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2160 }
2161
Simon Kelley316e2732010-01-22 20:16:09 +00002162 ret = &fake_opts[j--];
2163 ret->len = 1;
2164 ret->opt = SUBOPT_PXE_DISCOVERY;
2165 ret->val= &discovery_control;
2166
Simon Kelley7622fc02009-06-04 20:32:05 +01002167 return ret;
2168}
2169
2170static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2171{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002172 memset(mess->sname, 0, sizeof(mess->sname));
2173 memset(mess->file, 0, sizeof(mess->file));
2174 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2175 mess->siaddr.s_addr = 0;
2176}
Simon Kelleycdeda282006-03-16 20:16:06 +00002177
Simon Kelley7622fc02009-06-04 20:32:05 +01002178struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002179{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002180 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002181
2182 /* decide which dhcp-boot option we're using */
2183 for (boot = daemon->boot_config; boot; boot = boot->next)
2184 if (match_netid(boot->netid, netid, 0))
2185 break;
2186 if (!boot)
2187 /* No match, look for one without a netid */
2188 for (boot = daemon->boot_config; boot; boot = boot->next)
2189 if (match_netid(boot->netid, netid, 1))
2190 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002191
2192 return boot;
2193}
2194
2195static void do_options(struct dhcp_context *context,
2196 struct dhcp_packet *mess,
2197 unsigned char *end,
2198 unsigned char *req_options,
2199 char *hostname,
2200 char *domain, char *config_domain,
2201 struct dhcp_netid *netid,
2202 struct in_addr subnet_addr,
2203 unsigned char fqdn_flags,
2204 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002205 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002206 int vendor_class_len,
2207 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002208{
2209 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2210 struct dhcp_boot *boot;
2211 unsigned char *p;
2212 int i, len, force_encap = 0;
2213 unsigned char f0 = 0, s0 = 0;
2214 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002215 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002216 struct dhcp_netid *tagif;
2217 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002218
Simon Kelley7de060b2011-08-26 17:24:52 +01002219 /* flag options which are valid with the current tag set (sans context tags) */
2220 tagif = run_tag_if(netid);
2221 for (opt = config_opts; opt; opt = opt->next)
2222 {
2223 opt->flags &= ~DHOPT_TAGOK;
2224 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
2225 match_netid(opt->netid, tagif, 0))
2226 opt->flags |= DHOPT_TAGOK;
2227 }
2228
2229 /* now flag options which are valid, including the context tags,
2230 otherwise valid options are inhibited if we found a higher priotity one above */
2231 if (context && context->netid.net)
2232 {
2233 context->netid.next = netid;
2234 tagif = run_tag_if(&context->netid);
2235
2236 for (opt = config_opts; opt; opt = opt->next)
2237 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
2238 match_netid(opt->netid, tagif, 0))
2239 {
2240 struct dhcp_opt *tmp;
2241 for (tmp = config_opts; tmp; tmp = tmp->next)
2242 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
2243 break;
2244 if (!tmp)
2245 opt->flags |= DHOPT_TAGOK;
2246 }
2247 }
2248
2249 /* now flag untagged options which are not overridden by tagged ones */
2250 for (opt = config_opts; opt; opt = opt->next)
2251 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
2252 {
2253 struct dhcp_opt *tmp;
2254 for (tmp = config_opts; tmp; tmp = tmp->next)
2255 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
2256 break;
2257 if (!tmp)
2258 opt->flags |= DHOPT_TAGOK;
2259 else if (!tmp->netid)
2260 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
2261 }
2262
Simon Kelley7622fc02009-06-04 20:32:05 +01002263 if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
2264 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
Simon Kelley1ab84e22004-01-29 16:48:35 +00002265
Simon Kelley7622fc02009-06-04 20:32:05 +01002266 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002267 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002268 {
2269 char *q = daemon->namebuff;
2270 for (i = 0; req_options[i] != OPTION_END; i++)
2271 {
2272 char *s = option_string(req_options[i], NULL, NULL);
2273 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2274 "%d%s%s%s",
2275 req_options[i],
2276 s ? ":" : "",
2277 s ? s : "",
2278 req_options[i+1] == OPTION_END ? "" : ", ");
2279 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2280 {
2281 q = daemon->namebuff;
2282 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2283 }
2284 }
2285 }
2286
Simon Kelley7de060b2011-08-26 17:24:52 +01002287 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2288 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2289 break;
2290 if (id_list)
2291 mess->flags |= htons(0x8000); /* force broadcast */
2292
Simon Kelley73a08a22009-02-05 20:28:08 +00002293 if (context)
2294 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002295
2296 /* See if we can send the boot stuff as options.
2297 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002298 and very old DHCP clients won't have this, we also
2299 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002300 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002301 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002302 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002303 {
2304 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002305 {
Simon Kelley28866e92011-02-14 20:19:14 +00002306 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002307 req_options &&
2308 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002309 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2310 else
Simon Kelley824af852008-02-12 20:43:05 +00002311 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002312 }
2313
2314 if (boot->file)
2315 {
Simon Kelley28866e92011-02-14 20:19:14 +00002316 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002317 req_options &&
2318 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002319 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2320 else
Simon Kelley824af852008-02-12 20:43:05 +00002321 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002322 }
2323
Simon Kelley7de060b2011-08-26 17:24:52 +01002324 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002325 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002326 else if (boot->tftp_sname)
2327 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002328 }
Simon Kelley824af852008-02-12 20:43:05 +00002329 else
2330 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002331 they're not explicitly asked for as options. OPTION_END is used
2332 as an internal way to specify siaddr without using dhcp-boot, for use in
2333 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002334 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002335 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002336 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002337 {
2338 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2339 done_file = 1;
2340 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002341
Simon Kelley824af852008-02-12 20:43:05 +00002342 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002343 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002344 {
2345 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2346 done_server = 1;
2347 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002348
Simon Kelley7de060b2011-08-26 17:24:52 +01002349 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002350 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002351 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002352
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002353 /* We don't want to do option-overload for BOOTP, so make the file and sname
2354 fields look like they are in use, even when they aren't. This gets restored
2355 at the end of this function. */
2356
Simon Kelley28866e92011-02-14 20:19:14 +00002357 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002358 {
2359 f0 = mess->file[0];
2360 mess->file[0] = 1;
2361 s0 = mess->sname[0];
2362 mess->sname[0] = 1;
2363 }
2364
2365 /* At this point, if mess->sname or mess->file are zeroed, they are available
2366 for option overload, reserve space for the overload option. */
2367 if (mess->file[0] == 0 || mess->sname[0] == 0)
2368 end -= 3;
2369
Simon Kelley3be34542004-09-11 19:12:13 +01002370 /* rfc3011 says this doesn't need to be in the requested options list. */
2371 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002372 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002373
2374 /* replies to DHCPINFORM may not have a valid context */
2375 if (context)
2376 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002377 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002378 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2379
2380 /* May not have a "guessed" broadcast address if we got no packets via a relay
2381 from this net yet (ie just unicast renewals after a restart */
2382 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002383 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002384 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2385
2386 /* Same comments as broadcast apply, and also may not be able to get a sensible
2387 default when using subnet select. User must configure by steam in that case. */
2388 if (context->router.s_addr &&
2389 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002390 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002391 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2392
2393 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002394 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002395 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2396 }
Simon Kelley3be34542004-09-11 19:12:13 +01002397
Simon Kelley9009d742008-11-14 20:04:27 +00002398 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002399 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002400 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002401
Simon Kelley824af852008-02-12 20:43:05 +00002402 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002403 if (hostname)
2404 {
Simon Kelley824af852008-02-12 20:43:05 +00002405 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002406 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002407 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002408
2409 if (fqdn_flags != 0)
2410 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002411 len = strlen(hostname) + 3;
2412
Simon Kelley3d8df262005-08-29 12:19:27 +01002413 if (fqdn_flags & 0x04)
2414 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002415 else if (null_term)
2416 len++;
2417
Simon Kelley9009d742008-11-14 20:04:27 +00002418 if (domain)
2419 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002420
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002421 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002422 {
Simon Kelley3d8df262005-08-29 12:19:27 +01002423 *(p++) = fqdn_flags;
2424 *(p++) = 255;
2425 *(p++) = 255;
2426
2427 if (fqdn_flags & 0x04)
2428 {
2429 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002430 if (domain)
2431 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002432 *p++ = 0;
2433 }
2434 else
2435 {
2436 memcpy(p, hostname, strlen(hostname));
2437 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002438 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002439 {
2440 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002441 memcpy(p, domain, strlen(domain));
2442 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002443 }
2444 if (null_term)
2445 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002446 }
2447 }
2448 }
2449 }
2450
Simon Kelley6b010842007-02-12 20:32:07 +00002451 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002452 {
Simon Kelley824af852008-02-12 20:43:05 +00002453 int optno = opt->opt;
2454
Simon Kelley7de060b2011-08-26 17:24:52 +01002455 /* netids match and not encapsulated? */
2456 if (!(opt->flags & DHOPT_TAGOK))
2457 continue;
2458
Simon Kelley6b010842007-02-12 20:32:07 +00002459 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002460 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002461 continue;
2462
2463 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002464 if (optno == OPTION_CLIENT_FQDN ||
2465 optno == OPTION_MAXMESSAGE ||
2466 optno == OPTION_OVERLOAD ||
2467 optno == OPTION_PAD ||
2468 optno == OPTION_END)
2469 continue;
2470
2471 if (optno == OPTION_SNAME && done_server)
2472 continue;
2473
2474 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002475 continue;
2476
Simon Kelley33820b72004-04-03 21:10:00 +01002477 /* For the options we have default values on
2478 dhc-option=<optionno> means "don't include this option"
2479 not "include a zero-length option" */
2480 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002481 (optno == OPTION_NETMASK ||
2482 optno == OPTION_BROADCAST ||
2483 optno == OPTION_ROUTER ||
2484 optno == OPTION_DNSSERVER ||
2485 optno == OPTION_DOMAINNAME ||
2486 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002487 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002488
2489 /* vendor-class comes from elsewhere for PXE */
2490 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2491 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002492
Simon Kelley7622fc02009-06-04 20:32:05 +01002493 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002494 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002495 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002496
Simon Kelley824af852008-02-12 20:43:05 +00002497 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002498 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002499 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002500 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2501
Simon Kelley6b010842007-02-12 20:32:07 +00002502 /* If we send a vendor-id, revisit which vendor-ops we consider
2503 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002504 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002505 {
2506 match_vendor_opts(p - 2, config_opts);
2507 done_vendor_class = 1;
2508 }
Simon Kelley6b010842007-02-12 20:32:07 +00002509 }
2510 }
2511
Simon Kelley73a08a22009-02-05 20:28:08 +00002512 /* Now send options to be encapsulated in arbitrary options,
2513 eg dhcp-option=encap:172,17,.......
Simon Kelley316e2732010-01-22 20:16:09 +00002514 Also hand vendor-identifying vendor-encapsulated options,
2515 dhcp-option = rfc3925-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002516 The may be more that one "outer" to do, so group
2517 all the options which match each outer in turn. */
2518 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002519 opt->flags &= ~DHOPT_ENCAP_DONE;
2520
2521 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002522 {
2523 int flags;
2524
2525 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2526 {
2527 int found = 0;
2528 struct dhcp_opt *o;
2529
2530 if (opt->flags & DHOPT_ENCAP_DONE)
2531 continue;
2532
2533 for (len = 0, o = config_opts; o; o = o->next)
2534 {
2535 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2536
2537 o->flags &= ~DHOPT_ENCAP_MATCH;
2538
2539 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2540 continue;
2541
2542 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002543 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002544 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2545 {
2546 o->flags |= DHOPT_ENCAP_MATCH;
2547 found = 1;
2548 len += do_opt(o, NULL, NULL, 0) + 2;
2549 }
2550 }
2551
2552 if (found)
2553 {
2554 if (flags & DHOPT_ENCAPSULATE)
2555 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2556 else if (len > 250)
2557 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2558 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2559 {
2560 int swap_ent = htonl(opt->u.encap);
2561 memcpy(p, &swap_ent, 4);
2562 p += 4;
2563 *(p++) = len;
2564 for (o = config_opts; o; o = o->next)
2565 if (o->flags & DHOPT_ENCAP_MATCH)
2566 {
2567 len = do_opt(o, p + 2, NULL, 0);
2568 *(p++) = o->opt;
2569 *(p++) = len;
2570 p += len;
2571 }
2572 }
2573 }
2574 }
2575 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002576
Simon Kelley7de060b2011-08-26 17:24:52 +01002577 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002578
Simon Kelley316e2732010-01-22 20:16:09 +00002579 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002580 {
2581 pxe_misc(mess, end, uuid);
Simon Kelley7de060b2011-08-26 17:24:52 +01002582 config_opts = pxe_opts(pxe_arch, tagif, context->local);
Simon Kelley7622fc02009-06-04 20:32:05 +01002583 }
2584
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002585 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2586 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2587 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2588 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2589 /* If we send vendor encapsulated options, and haven't already sent option 60,
2590 echo back the value we got from the client. */
2591 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2592
Simon Kelley7622fc02009-06-04 20:32:05 +01002593 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002594 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002595 {
2596 mess->file[0] = f0;
2597 mess->sname[0] = s0;
2598 }
2599}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002600
Simon Kelley7622fc02009-06-04 20:32:05 +01002601#endif
2602
2603
2604
2605
2606
2607
2608