blob: 0dc06ab2541ad969886638ba88a4a167c7bace23 [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 Kelley5e9e0ef2006-04-17 14:24:29 +010021#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010022#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000023
Simon Kelley316e2732010-01-22 20:16:09 +000024#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +000025static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt);
26#endif
Simon Kelley572b41e2011-02-18 18:11:18 +000027
Simon Kelleyf2621c72007-04-29 19:47:21 +010028static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000029static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000030static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000031static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
32static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
33 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000034static struct in_addr option_addr(unsigned char *opt);
Simon Kelley7622fc02009-06-04 20:32:05 +010035static unsigned int option_uint(unsigned char *opt, int i, int size);
36static void log_packet(char *type, void *addr, unsigned char *ext_mac,
37 int mac_len, char *interface, char *string, u32 xid);
Simon Kelleycdeda282006-03-16 20:16:06 +000038static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010039static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley7de060b2011-08-26 17:24:52 +010040static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +010041static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000042static void do_options(struct dhcp_context *context,
43 struct dhcp_packet *mess,
44 unsigned char *real_end,
45 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000046 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +000047 char *config_domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000048 struct dhcp_netid *netid,
Simon Kelley7de060b2011-08-26 17:24:52 +010049 struct in_addr subnet_addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000050 unsigned char fqdn_flags,
Simon Kelley7622fc02009-06-04 20:32:05 +010051 int null_term, int pxearch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +010052 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +010053 int vendor_class_len,
54 time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010055
Simon Kelley9009d742008-11-14 20:04:27 +000056
Simon Kelley6b010842007-02-12 20:32:07 +000057static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Simon Kelley8ef5ada2010-06-03 19:42:45 +010058static 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 +010059static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
60static int prune_vendor_opts(struct dhcp_netid *netid);
Simon Kelley751d6f42012-02-10 15:24:51 +000061static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
Simon Kelley7622fc02009-06-04 20:32:05 +010062struct dhcp_boot *find_boot(struct dhcp_netid *netid);
63
64
Simon Kelley824af852008-02-12 20:43:05 +000065size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley7de060b2011-08-26 17:24:52 +010066 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 +010067{
Simon Kelley26128d22004-11-14 16:43:54 +000068 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +000069 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +010070 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +000071 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +000072 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +010073 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
Simon Kelley824af852008-02-12 20:43:05 +000074 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000075 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley7622fc02009-06-04 20:32:05 +010076 unsigned char *real_end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +000077 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +000078 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010079 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000080 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +000081 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000082 struct dhcp_config *config;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010083 struct dhcp_netid *netid, *tagif_netid;
Simon Kelley7de060b2011-08-26 17:24:52 +010084 struct in_addr subnet_addr, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010085 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010086 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +010087 unsigned char fqdn_flags = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +010088 unsigned char *agent_id = NULL, *uuid = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +000089 unsigned char *emac = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +010090 int vendor_class_len = 0, emac_len = 0;
Simon Kelley316e2732010-01-22 20:16:09 +000091 struct dhcp_netid known_id, iface_id, cpewan_id;
Simon Kelley73a08a22009-02-05 20:28:08 +000092 struct dhcp_opt *o;
Simon Kelley7622fc02009-06-04 20:32:05 +010093 unsigned char pxe_uuid[17];
Simon Kelley316e2732010-01-22 20:16:09 +000094 unsigned char *oui = NULL, *serial = NULL, *class = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +010095
Simon Kelley1a6bca82008-07-11 11:11:42 +010096 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +000097
98 /* set tag with name == interface */
99 iface_id.net = iface_name;
100 iface_id.next = NULL;
101 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100102
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100103 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100104 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000105
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100106 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000107 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100108
Simon Kelley3be34542004-09-11 19:12:13 +0100109 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000110 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000111 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100112 u32 cookie = htonl(DHCP_COOKIE);
113
Simon Kelley3be34542004-09-11 19:12:13 +0100114 /* only insist on a cookie for DHCP. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100115 if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100116 return 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100117
118 mess_type = option_uint(opt, 0, 1);
119
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100120 /* two things to note here: expand_buf may move the packet,
121 so reassign mess from daemon->packet. Also, the size
122 sent includes the IP and UDP headers, hence the magic "-28" */
123 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
124 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100125 size_t size = (size_t)option_uint(opt, 0, 2) - 28;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100126
127 if (size > DHCP_PACKET_MAX)
128 size = DHCP_PACKET_MAX;
129 else if (size < sizeof(struct dhcp_packet))
130 size = sizeof(struct dhcp_packet);
131
132 if (expand_buf(&daemon->dhcp_packet, size))
133 {
Simon Kelley824af852008-02-12 20:43:05 +0000134 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley7622fc02009-06-04 20:32:05 +0100135 real_end = end = ((unsigned char *)mess) + size;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100136 }
137 }
138
Simon Kelley3be34542004-09-11 19:12:13 +0100139 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
140 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000141 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100142 mess->ciaddr.s_addr = 0;
143
Simon Kelley316e2732010-01-22 20:16:09 +0000144 /* search for device identity from CPEWAN devices, we pass this through to the script */
145 if ((opt = option_find(mess, sz, OPTION_VENDOR_IDENT_OPT, 5)))
146 {
147 unsigned int elen, offset, len = option_len(opt);
148
149 for (offset = 0; offset < (len - 5); offset += elen + 5)
150 {
151 elen = option_uint(opt, offset + 4 , 1);
152 if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)
153 {
154 unsigned char *x = option_ptr(opt, offset + 5);
155 unsigned char *y = option_ptr(opt, offset + elen + 5);
156 oui = option_find1(x, y, 1, 1);
157 serial = option_find1(x, y, 2, 1);
158 class = option_find1(x, y, 3, 1);
159
160 /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing
161 the gateway id back. Note that the device class is optional */
162 if (oui && serial)
163 {
164 cpewan_id.net = "cpewan-id";
165 cpewan_id.next = netid;
166 netid = &cpewan_id;
167 }
168 break;
169 }
170 }
171 }
172
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100173 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
174 {
175 /* Any agent-id needs to be copied back out, verbatim, as the last option
176 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
177 get overwritten, then it will be shuffled back at the end of processing.
178 Note that the incoming options must not be overwritten here, so there has to
179 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100180 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100181 unsigned int total = option_len(opt) + 2;
182 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
183 if (last_opt && last_opt < end - total)
184 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100185 end -= total;
186 agent_id = end;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100187 memcpy(agent_id, opt, total);
188 }
189
190 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100191 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 +0100192 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100193
194 /* look for RFC5107 server-identifier-override */
195 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
196 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100197
198 /* if a circuit-id or remote-is option is provided, exact-match to options. */
199 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
200 {
201 int search;
202
203 if (vendor->match_type == MATCH_CIRCUIT)
204 search = SUBOPT_CIRCUIT_ID;
205 else if (vendor->match_type == MATCH_REMOTE)
206 search = SUBOPT_REMOTE_ID;
207 else if (vendor->match_type == MATCH_SUBSCRIBER)
208 search = SUBOPT_SUBSCR_ID;
209 else
210 continue;
211
Simon Kelley1a6bca82008-07-11 11:11:42 +0100212 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100213 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100214 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100215 {
216 vendor->netid.next = netid;
217 netid = &vendor->netid;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100218 }
219 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100220 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100221
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100222 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
223 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100224 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000225
226 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000227 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000228 {
Simon Kelley26128d22004-11-14 16:43:54 +0000229 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100230 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000231 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000232
Simon Kelley0a852542005-03-23 20:28:59 +0000233 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100234 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000235
236 /* If this request is missing a clid, but we've seen one before,
237 use it again for option matching etc. */
238 if (lease && !clid && lease->clid)
239 {
240 clid_len = lease->clid_len;
241 clid = lease->clid;
242 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000243
244 /* find mac to use for logging and hashing */
245 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000246 }
Simon Kelley3be34542004-09-11 19:12:13 +0100247
Simon Kelleycdeda282006-03-16 20:16:06 +0000248 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
249 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100250 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
251 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000252 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100253 mac->netid.next = netid;
254 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000255 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100256
Simon Kelley0a852542005-03-23 20:28:59 +0000257 /* Determine network for this packet. Our caller will have already linked all the
258 contexts which match the addresses of the receiving interface but if the
259 machine has an address already, or came via a relay, or we have a subnet selector,
260 we search again. If we don't have have a giaddr or explicit subnet selector,
261 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100262 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
263 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000264 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
265 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100266 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100267 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100268 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000269
Simon Kelley3d8df262005-08-29 12:19:27 +0100270 if (subnet_addr.s_addr)
271 {
272 addr = subnet_addr;
273 force = 1;
274 }
275 else if (mess->giaddr.s_addr)
276 {
277 addr = mess->giaddr;
278 force = 1;
279 }
Simon Kelley16972692006-10-16 20:04:18 +0100280 else
281 {
282 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
283 addr = mess->ciaddr;
284 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
285 if (context_tmp->netmask.s_addr &&
286 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
287 is_same_net(addr, context_tmp->end, context_tmp->netmask))
288 {
289 context_new = context;
290 break;
291 }
292 }
293
294 if (!context_new)
295 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
Simon Kelley7de060b2011-08-26 17:24:52 +0100296 {
297 struct in_addr netmask = context_tmp->netmask;
298
299 /* guess the netmask for relayed networks */
300 if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
301 {
302 if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
303 netmask.s_addr = htonl(0xff000000);
304 else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
305 netmask.s_addr = htonl(0xffff0000);
306 else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
307 netmask.s_addr = htonl(0xffffff00);
308 }
309
310 /* This section fills in context mainly when a client which is on a remote (relayed)
311 network renews a lease without using the relay, after dnsmasq has restarted. */
312 if (netmask.s_addr != 0 &&
313 is_same_net(addr, context_tmp->start, netmask) &&
314 is_same_net(addr, context_tmp->end, netmask))
315 {
316 context_tmp->netmask = netmask;
317 if (context_tmp->local.s_addr == 0)
318 context_tmp->local = fallback;
319 if (context_tmp->router.s_addr == 0)
320 context_tmp->router = mess->giaddr;
321
322 /* fill in missing broadcast addresses for relayed ranges */
323 if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
324 context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
325
326 context_tmp->current = context_new;
327 context_new = context_tmp;
328 }
329 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100330
Simon Kelley3d8df262005-08-29 12:19:27 +0100331 if (context_new || force)
Simon Kelley7de060b2011-08-26 17:24:52 +0100332 context = context_new;
Simon Kelley0a852542005-03-23 20:28:59 +0000333 }
Simon Kelley3be34542004-09-11 19:12:13 +0100334
335 if (!context)
336 {
Simon Kelley7622fc02009-06-04 20:32:05 +0100337 my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100338 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
339 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 +0100340 return 0;
341 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100342
Simon Kelley28866e92011-02-14 20:19:14 +0000343 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100344 {
345 struct dhcp_context *context_tmp;
Simon Kelleyf2621c72007-04-29 19:47:21 +0100346 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
347 {
348 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
Simon Kelley7622fc02009-06-04 20:32:05 +0100349 if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100350 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100351 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100352 else
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100353 my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
Simon Kelley7622fc02009-06-04 20:32:05 +0100354 ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
Simon Kelleyf2621c72007-04-29 19:47:21 +0100355 }
356 }
357
Simon Kelley3be34542004-09-11 19:12:13 +0100358 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100359
Simon Kelleycdeda282006-03-16 20:16:06 +0000360 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
361 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100362
363 /* set "known" tag for known hosts */
364 if (config)
365 {
366 known_id.net = "known";
367 known_id.next = netid;
368 netid = &known_id;
369 }
Simon Kelley26128d22004-11-14 16:43:54 +0000370
Simon Kelley316e2732010-01-22 20:16:09 +0000371 if (mess_type == 0 && !pxe)
Simon Kelley3be34542004-09-11 19:12:13 +0100372 {
373 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000374 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000375 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100376
377 /* must have a MAC addr for bootp */
Simon Kelley7622fc02009-06-04 20:32:05 +0100378 if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100379 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000380
Simon Kelley26128d22004-11-14 16:43:54 +0000381 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000382 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000383
384 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
385
386 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000387 {
388 hostname = config->hostname;
389 domain = config->domain;
390 }
391
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100392 if (config)
Simon Kelley26128d22004-11-14 16:43:54 +0000393 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100394 struct dhcp_netid_list *list;
395
396 for (list = config->netid; list; list = list->next)
397 {
398 list->list->next = netid;
399 netid = list->list;
400 }
Simon Kelley26128d22004-11-14 16:43:54 +0000401 }
402
403 /* Match incoming filename field as a netid. */
404 if (mess->file[0])
405 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000406 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
407 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
408 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000409 id.next = netid;
410 netid = &id;
411 }
Simon Kelley6b010842007-02-12 20:32:07 +0000412
413 /* Add "bootp" as a tag to allow different options, address ranges etc
414 for BOOTP clients */
415 bootp_id.net = "bootp";
416 bootp_id.next = netid;
417 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000418
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100419 tagif_netid = run_tag_if(netid);
420
Simon Kelley26128d22004-11-14 16:43:54 +0000421 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100422 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley1f15b812009-10-13 17:49:32 +0100423 message = _("ignored");
Simon Kelley26128d22004-11-14 16:43:54 +0000424
Simon Kelley3d8df262005-08-29 12:19:27 +0100425 if (!message)
426 {
Simon Kelley9009d742008-11-14 20:04:27 +0000427 int nailed = 0;
428
Simon Kelley3d8df262005-08-29 12:19:27 +0100429 if (have_config(config, CONFIG_ADDR))
430 {
Simon Kelley9009d742008-11-14 20:04:27 +0000431 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100432 logaddr = &config->addr;
433 mess->yiaddr = config->addr;
434 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000435 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100436 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000437 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000438 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100439 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100440 else
441 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000442 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100443 !address_available(context, lease->addr, tagif_netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000444 {
445 if (lease)
446 {
447 /* lease exists, wrong network. */
448 lease_prune(lease, now);
449 lease = NULL;
450 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100451 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000452 message = _("no address available");
453 }
454 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100455 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100456 }
457
Simon Kelley9009d742008-11-14 20:04:27 +0000458 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
459 message = _("wrong network");
460 else if (context->netid.net)
461 {
462 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +0100463 tagif_netid = run_tag_if(&context->netid);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100464 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100465
Simon Kelley4cb1b322012-02-06 14:30:41 +0000466 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100467
Simon Kelley9009d742008-11-14 20:04:27 +0000468 if (!message && !nailed)
469 {
470 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100471 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley9009d742008-11-14 20:04:27 +0000472 break;
473 if (!id_list)
474 message = _("no address configured");
475 }
476
Simon Kelley7cebd202006-05-06 14:13:33 +0100477 if (!message &&
478 !lease &&
Simon Kelley52b92f42012-01-22 16:05:15 +0000479 (!(lease = lease4_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000480 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000481
Simon Kelley3d8df262005-08-29 12:19:27 +0100482 if (!message)
483 {
484 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100485
Simon Kelleycdeda282006-03-16 20:16:06 +0000486 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100487 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000488 lease_set_hostname(lease, hostname, 1, get_domain(lease->addr), domain);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100489 /* infinite lease unless nailed in dhcp-host line. */
490 lease_set_expires(lease,
491 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
492 now);
Simon Kelley824af852008-02-12 20:43:05 +0000493 lease_set_interface(lease, int_index);
Simon Kelley3d8df262005-08-29 12:19:27 +0100494
Simon Kelley7622fc02009-06-04 20:32:05 +0100495 clear_packet(mess, end);
Simon Kelley9009d742008-11-14 20:04:27 +0000496 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +0000497 netid, subnet_addr, 0, 0, 0, NULL, 0, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100498 }
499 }
500
Simon Kelley7622fc02009-06-04 20:32:05 +0100501 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000502
Simon Kelley7de060b2011-08-26 17:24:52 +0100503 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100504 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000505
Simon Kelley3d8df262005-08-29 12:19:27 +0100506 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
507 {
508 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
509 int len = option_len(opt);
510 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100511 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100512
513 fqdn_flags = *op;
514 len -= 3;
515 op += 3;
516 pp = op;
517
518 /* Always force update, since the client has no way to do it itself. */
Simon Kelleyc72daea2012-01-05 21:33:27 +0000519 if (!option_bool(OPT_FQDN_UPDATE) && !(fqdn_flags & 0x01))
520 fqdn_flags |= 0x03;
521
Simon Kelley3d8df262005-08-29 12:19:27 +0100522 fqdn_flags &= ~0x08;
Simon Kelley3d8df262005-08-29 12:19:27 +0100523
524 if (fqdn_flags & 0x04)
525 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
526 {
527 memcpy(pq, op+1, *op);
528 pq += *op;
529 op += (*op)+1;
530 *(pq++) = '.';
531 }
532 else
533 {
534 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000535 if (len > 0 && op[len-1] == 0)
536 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100537 pq += len + 1;
538 }
539
540 if (pq != daemon->dhcp_buff)
541 pq--;
542
543 *pq = 0;
544
Simon Kelley1f15b812009-10-13 17:49:32 +0100545 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100546 offer_hostname = client_hostname = daemon->dhcp_buff;
547 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000548 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000549 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000550 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100551 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000552 /* Microsoft clients are broken, and need zero-terminated strings
553 in options. We detect this state here, and do the same in
554 any options we send */
555 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
556 borken_opt = 1;
557 else
558 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100559 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100560 client_hostname = daemon->dhcp_buff;
561 }
562
Simon Kelley28866e92011-02-14 20:19:14 +0000563 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100564 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
565
Simon Kelley3d8df262005-08-29 12:19:27 +0100566 if (have_config(config, CONFIG_NAME))
567 {
568 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000569 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000570 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000571 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100572 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000573 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100574 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100575 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100576 {
Simon Kelley9009d742008-11-14 20:04:27 +0000577 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100578
579 if (strlen(client_hostname) != 0)
580 {
581 hostname = client_hostname;
582 if (!config)
583 {
584 /* Search again now we have a hostname.
585 Only accept configs without CLID and HWADDR here, (they won't match)
586 to avoid impersonation by name. */
587 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
588 mess->chaddr, mess->hlen,
589 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000590 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000591 {
592 config = new;
593 /* set "known" tag for known hosts */
594 known_id.net = "known";
595 known_id.next = netid;
596 netid = &known_id;
597 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100598 }
599 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000600 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100601
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100602 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100603 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100604 struct dhcp_netid_list *list;
605
606 for (list = config->netid; list; list = list->next)
607 {
608 list->list->next = netid;
609 netid = list->list;
610 }
Simon Kelleya2226412004-05-13 20:27:08 +0100611 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100612
Simon Kelley73a08a22009-02-05 20:28:08 +0000613 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
614 Otherwise assume the option is an array, and look for a matching element.
Simon Kelley316e2732010-01-22 20:16:09 +0000615 If no data given, existance of the option is enough. This code handles
616 rfc3925 V-I classes too. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000617 for (o = daemon->dhcp_match; o; o = o->next)
618 {
Simon Kelley316e2732010-01-22 20:16:09 +0000619 unsigned int len, elen, match = 0;
620 size_t offset, o2;
Simon Kelley73a08a22009-02-05 20:28:08 +0000621
Simon Kelley316e2732010-01-22 20:16:09 +0000622 if (o->flags & DHOPT_RFC3925)
623 {
624 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
625 continue;
626
627 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
628 {
629 len = option_uint(opt, offset + 4 , 1);
630 /* Need to take care that bad data can't run us off the end of the packet */
631 if ((offset + len + 5 <= (option_len(opt))) &&
632 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
633 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
634 {
635 elen = option_uint(opt, o2, 1);
636 if ((o2 + elen + 1 <= option_len(opt)) &&
637 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
638 break;
639 }
640 if (match)
641 break;
642 }
643 }
644 else
645 {
646 if (!(opt = option_find(mess, sz, o->opt, 1)))
647 continue;
648
649 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
650 }
651
652 if (match)
Simon Kelley73a08a22009-02-05 20:28:08 +0000653 {
654 o->netid->next = netid;
655 netid = o->netid;
656 }
657 }
658
Simon Kelley26128d22004-11-14 16:43:54 +0000659 /* user-class options are, according to RFC3004, supposed to contain
660 a set of counted strings. Here we check that this is so (by seeing
661 if the counts are consistent with the overall option length) and if
662 so zero the counts so that we don't get spurious matches between
663 the vendor string and the counts. If the lengths don't add up, we
664 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100665 and just do the substring match. dhclient provides these broken options.
666 The code, later, which sends user-class data to the lease-change script
667 relies on the transformation done here.
668 */
Simon Kelleya2226412004-05-13 20:27:08 +0100669
Simon Kelleybb01cb92004-12-13 20:56:23 +0000670 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100671 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100672 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000673 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000674 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
675 if (j == option_len(opt))
676 for (j = 0; j < option_len(opt); j = tmp)
677 {
678 tmp = j + ucp[j] + 1;
679 ucp[j] = 0;
680 }
Simon Kelleya2226412004-05-13 20:27:08 +0100681 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000682
Simon Kelley26128d22004-11-14 16:43:54 +0000683 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100684 {
685 int mopt;
686
687 if (vendor->match_type == MATCH_VENDOR)
688 mopt = OPTION_VENDOR_ID;
689 else if (vendor->match_type == MATCH_USER)
690 mopt = OPTION_USER_CLASS;
691 else
692 continue;
693
694 if ((opt = option_find(mess, sz, mopt, 1)))
695 {
696 int i;
697 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100698 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100699 {
700 vendor->netid.next = netid;
701 netid = &vendor->netid;
702 break;
703 }
704 }
705 }
706
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100707 /* mark vendor-encapsulated options which match the client-supplied vendor class,
708 save client-supplied vendor class */
709 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
710 {
711 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
712 vendor_class_len = option_len(opt);
713 }
714 match_vendor_opts(opt, daemon->dhcp_opts);
715
Simon Kelley28866e92011-02-14 20:19:14 +0000716 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100717 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100718 if (sanitise(opt, daemon->namebuff))
719 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100720 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100721 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100722 }
723
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100724 tagif_netid = run_tag_if(netid);
725
Simon Kelley26128d22004-11-14 16:43:54 +0000726 /* if all the netids in the ignore list are present, ignore this client */
727 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100728 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000729 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100730
731 /* If configured, we can override the server-id to be the address of the relay,
732 so that all traffic goes via the relay and can pick up agent-id info. This can be
733 configured for all relays, or by address. */
734 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
735 {
736 if (!daemon->override_relays)
737 override = mess->giaddr;
738 else
739 {
740 struct addr_list *l;
741 for (l = daemon->override_relays; l; l = l->next)
742 if (l->addr.s_addr == mess->giaddr.s_addr)
743 break;
744 if (l)
745 override = mess->giaddr;
746 }
747 }
748
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100749 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
750 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000751 clid = NULL;
752
Simon Kelley7622fc02009-06-04 20:32:05 +0100753 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100754 if (daemon->enable_pxe &&
755 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100756 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
757 {
758 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
759 {
760 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
761 uuid = pxe_uuid;
762 }
763
764 /* Check if this is really a PXE bootserver request, and handle specially if so. */
765 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
766 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
767 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
768 {
769 struct pxe_service *service;
770 int type = option_uint(opt, 0, 2);
771 int layer = option_uint(opt, 2, 2);
772 unsigned char save71[4];
773 struct dhcp_opt opt71;
774
Simon Kelley1f15b812009-10-13 17:49:32 +0100775 if (ignore)
776 return 0;
777
Simon Kelley7622fc02009-06-04 20:32:05 +0100778 if (layer & 0x8000)
779 {
780 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
781 return 0;
782 }
783
784 memcpy(save71, option_ptr(opt, 0), 4);
785
786 for (service = daemon->pxe_services; service; service = service->next)
787 if (service->type == type)
788 break;
789
790 if (!service || !service->basename)
791 return 0;
792
793 clear_packet(mess, end);
794
795 mess->yiaddr = mess->ciaddr;
796 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000797 if (service->sname)
798 mess->siaddr = a_record_from_hosts(service->sname, now);
799 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100800 mess->siaddr = service->server;
801 else
802 mess->siaddr = context->local;
803
804 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
805 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
806 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
807 pxe_misc(mess, end, uuid);
808
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100809 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100810 opt71.val = save71;
811 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
812 opt71.len = 4;
813 opt71.flags = DHOPT_VENDOR_MATCH;
814 opt71.netid = NULL;
815 opt71.next = daemon->dhcp_opts;
816 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
817
818 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000819 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100820 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100821 }
822
823 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
824 {
825 pxearch = option_uint(opt, 0, 2);
826
Simon Kelley316e2732010-01-22 20:16:09 +0000827 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000828 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100829 {
Simon Kelley28866e92011-02-14 20:19:14 +0000830 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100831
Simon Kelley28866e92011-02-14 20:19:14 +0000832 for (tmp = context; tmp; tmp = tmp->current)
833 if ((tmp->flags & CONTEXT_PROXY) &&
834 match_netid(tmp->filter, tagif_netid, 1))
835 break;
836
837 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100838 {
Simon Kelley28866e92011-02-14 20:19:14 +0000839 struct dhcp_boot *boot = find_boot(tagif_netid);
840
841 mess->yiaddr.s_addr = 0;
842 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
843 {
844 mess->ciaddr.s_addr = 0;
845 mess->flags |= htons(0x8000); /* broadcast */
846 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100847
Simon Kelley28866e92011-02-14 20:19:14 +0000848 clear_packet(mess, end);
849
850 /* Provide the bootfile here, for gPXE, and in case we have no menu items
851 and set discovery_control = 8 */
852 if (boot)
853 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100854 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000855 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100856 else if (boot->tftp_sname)
857 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000858
859 if (boot->file)
860 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
861 }
862
863 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
864 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
865 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
866 pxe_misc(mess, end, uuid);
867 prune_vendor_opts(tagif_netid);
Simon Kelley751d6f42012-02-10 15:24:51 +0000868 do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
Simon Kelley28866e92011-02-14 20:19:14 +0000869
870 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000871 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100872 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100873 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100874 }
875 }
876 }
877
878 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000879 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100880 return 0;
881
Simon Kelleybb01cb92004-12-13 20:56:23 +0000882 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100883 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100884 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100885 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000886 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100887 }
888
Simon Kelley3be34542004-09-11 19:12:13 +0100889 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000890 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000891 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000892 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000893 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000894 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100895
Simon Kelley44a2a312004-03-10 20:04:35 +0000896 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100897 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000898
Simon Kelleybb01cb92004-12-13 20:56:23 +0000899 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000900 return 0;
901
Simon Kelley7622fc02009-06-04 20:32:05 +0100902 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000903
904 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
905 lease_prune(lease, now);
906
Simon Kelley33820b72004-04-03 21:10:00 +0100907 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000908 config->addr.s_addr == option_addr(opt).s_addr)
909 {
Simon Kelley849a8352006-06-09 21:02:31 +0100910 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100911 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100912 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100913 config->flags |= CONFIG_DECLINED;
914 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000915 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100916 else
917 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100918 for (; context; context = context->current)
919 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000920
921 return 0;
922
923 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100924 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100925 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000926 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000927 return 0;
928
Simon Kelley44a2a312004-03-10 20:04:35 +0000929 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
930 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100931 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000932 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100933
Simon Kelley7622fc02009-06-04 20:32:05 +0100934 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000935
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000936 return 0;
937
938 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100939 if (ignore || have_config(config, CONFIG_DISABLE))
940 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000941 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100942 opt = NULL;
943 }
944 else
945 {
946 struct in_addr addr, conf;
947
Simon Kelley1a6bca82008-07-11 11:11:42 +0100948 addr.s_addr = conf.s_addr = 0;
949
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100950 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
951 addr = option_addr(opt);
952
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100953 if (have_config(config, CONFIG_ADDR))
954 {
Simon Kelley849a8352006-06-09 21:02:31 +0100955 char *addrs = inet_ntoa(config->addr);
956
Simon Kelley9009d742008-11-14 20:04:27 +0000957 if ((ltmp = lease_find_by_addr(config->addr)) &&
958 ltmp != lease &&
959 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000960 {
961 int len;
962 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
963 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100964 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100965 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000966 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100967 else
968 {
969 struct dhcp_context *tmp;
970 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100971 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100972 break;
973 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100974 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 +0100975 else if (have_config(config, CONFIG_DECLINED) &&
976 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100977 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100978 else
979 conf = config->addr;
980 }
981 }
982
983 if (conf.s_addr)
984 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100985 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100986 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100987 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100988 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100989 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100990 !config_find_by_address(daemon->dhcp_conf, addr))
991 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100992 else if (emac_len == 0)
993 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100994 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100995 message = _("no address available");
996 }
997
Simon Kelley7622fc02009-06-04 20:32:05 +0100998 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +0100999
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001000 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001001 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001002
Simon Kelleycdeda282006-03-16 20:16:06 +00001003 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001004 {
1005 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001006 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001007 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001008
Simon Kelley4cb1b322012-02-06 14:30:41 +00001009 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001010
1011 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1012
Simon Kelley824af852008-02-12 20:43:05 +00001013 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001014 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001015 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001016 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001017 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001018 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001019 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001020 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001021 option_put(mess, end, OPTION_T1, 4, (time/2));
1022 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001023 }
Simon Kelley9009d742008-11-14 20:04:27 +00001024 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001025 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001026
Simon Kelley7de060b2011-08-26 17:24:52 +01001027 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001028
1029 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001030 if (ignore || have_config(config, CONFIG_DISABLE))
1031 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001032 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001033 {
1034 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001035 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001036
Simon Kelley4011c4e2006-10-28 16:26:19 +01001037 /* send vendor and user class info for new or recreated lease */
1038 do_classes = 1;
1039
Simon Kelleybb01cb92004-12-13 20:56:23 +00001040 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001041 {
Simon Kelley3be34542004-09-11 19:12:13 +01001042 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001043 selecting = 1;
1044
Simon Kelley1a6bca82008-07-11 11:11:42 +01001045 if (override.s_addr != 0)
1046 {
1047 if (option_addr(opt).s_addr != override.s_addr)
1048 return 0;
1049 }
Simon Kelley9009d742008-11-14 20:04:27 +00001050 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001051 {
1052 for (; context; context = context->current)
1053 if (context->local.s_addr == option_addr(opt).s_addr)
1054 break;
1055
1056 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001057 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001058 /* Handle very strange configs where clients have more than one route to the server.
1059 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1060 Have to set override to make sure we echo back the correct server-id */
1061 struct irec *intr;
1062
1063 enumerate_interfaces();
1064
1065 for (intr = daemon->interfaces; intr; intr = intr->next)
1066 if (intr->addr.sa.sa_family == AF_INET &&
1067 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1068 intr->tftp_ok)
1069 break;
1070
1071 if (intr)
1072 override = intr->addr.in.sin_addr;
1073 else
1074 {
1075 /* In auth mode, a REQUEST sent to the wrong server
1076 should be faulted, so that the client establishes
1077 communication with us, otherwise, silently ignore. */
1078 if (!option_bool(OPT_AUTHORITATIVE))
1079 return 0;
1080 message = _("wrong server-ID");
1081 }
Simon Kelley9009d742008-11-14 20:04:27 +00001082 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001083 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001084
Simon Kelley3be34542004-09-11 19:12:13 +01001085 /* If a lease exists for this host and another address, squash it. */
1086 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1087 {
1088 lease_prune(lease, now);
1089 lease = NULL;
1090 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001091 }
Simon Kelley3be34542004-09-11 19:12:13 +01001092 else
1093 {
1094 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001095 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001096 return 0;
1097
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001098 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001099 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001100 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001101 }
1102 else
1103 {
1104 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001105 /* Check existing lease for this address.
1106 We allow it to be missing if dhcp-authoritative mode
1107 as long as we can allocate the lease now - checked below.
1108 This makes for a smooth recovery from a lost lease DB */
1109 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001110 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001111 {
Simon Kelley28866e92011-02-14 20:19:14 +00001112 /* A client rebinding will broadcast the request, so we may see it even
1113 if the lease is held by another server. Just ignore it in that case.
1114 If the request is unicast to us, then somethings wrong, NAK */
1115 if (!unicast_dest)
1116 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001117 message = _("lease not found");
1118 /* ensure we broadcast NAK */
1119 unicast_dest = 0;
1120 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001121
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001122 /* desynchronise renewals */
1123 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001124 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001125 }
1126
Simon Kelley7622fc02009-06-04 20:32:05 +01001127 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001128
Simon Kelleydfa666f2004-08-02 18:27:27 +01001129 if (!message)
1130 {
1131 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001132 struct dhcp_context *tmp = NULL;
1133
1134 if (have_config(config, CONFIG_ADDR))
1135 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001136 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001137 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001138
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001139 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001140 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001141 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001142 message = _("wrong network");
1143 /* ensure we broadcast NAK */
1144 unicast_dest = 0;
1145 }
1146
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001147 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001148 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001149 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001150 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001151
Simon Kelleydfa666f2004-08-02 18:27:27 +01001152 /* Check if a new static address has been configured. Be very sure that
1153 when the client does DISCOVER, it will get the static address, otherwise
1154 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001155 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001156 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001157 (!have_config(config, CONFIG_DECLINED) ||
1158 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001159 config->addr.s_addr != mess->yiaddr.s_addr &&
1160 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001161 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001162
1163 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001164 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001165 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001166
Simon Kelley9009d742008-11-14 20:04:27 +00001167 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1168 {
1169 /* If a host is configured with more than one MAC address, it's OK to 'nix
1170 a lease from one of it's MACs to give the address to another. */
1171 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1172 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001173 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001174 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1175 inet_ntoa(ltmp->addr));
1176 lease = ltmp;
1177 }
Simon Kelley16972692006-10-16 20:04:18 +01001178 else
Simon Kelley9009d742008-11-14 20:04:27 +00001179 message = _("address in use");
1180 }
1181
1182 if (!message)
1183 {
1184 if (emac_len == 0)
1185 message = _("no unique-id");
1186
1187 else if (!lease)
1188 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001189 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001190 do_classes = 1;
1191 else
1192 message = _("no leases left");
1193 }
Simon Kelley16972692006-10-16 20:04:18 +01001194 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001195 }
Simon Kelley16972692006-10-16 20:04:18 +01001196
Simon Kelley44a2a312004-03-10 20:04:35 +00001197 if (message)
1198 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001199 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001200
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001201 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001202 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001203 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001204 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001205 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001206 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1207 a distant subnet which unicast a REQ to us won't work. */
1208 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1209 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1210 {
1211 mess->flags |= htons(0x8000); /* broadcast */
1212 mess->ciaddr.s_addr = 0;
1213 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001214 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001215 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001216 {
Simon Kelley316e2732010-01-22 20:16:09 +00001217 if (context->netid.net)
1218 {
1219 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001220 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001221 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001222
Simon Kelley4cb1b322012-02-06 14:30:41 +00001223 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001224
Simon Kelley316e2732010-01-22 20:16:09 +00001225#ifdef HAVE_SCRIPT
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001226 if (do_classes && daemon->lease_change_command)
1227 {
1228 struct dhcp_netid *n;
1229
1230 if (mess->giaddr.s_addr)
1231 lease->giaddr = mess->giaddr;
1232
Simon Kelley4cb1b322012-02-06 14:30:41 +00001233 lease->flags |= LEASE_CHANGED;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001234 free(lease->extradata);
Simon Kelley28866e92011-02-14 20:19:14 +00001235 lease->extradata = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001236 lease->extradata_size = lease->extradata_len = 0;
1237
1238 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1239 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1240 add_extradata_opt(lease, oui);
1241 add_extradata_opt(lease, serial);
1242 add_extradata_opt(lease, class);
1243
1244 /* space-concat tag set */
1245 if (!tagif_netid)
1246 add_extradata_opt(lease, NULL);
1247 else
1248 for (n = tagif_netid; n; n = n->next)
Simon Kelley39bec5f2012-01-06 22:36:58 +00001249 {
1250 struct dhcp_netid *n1;
1251 /* kill dupes */
1252 for (n1 = n->next; n1; n1 = n1->next)
1253 if (strcmp(n->net, n1->net) == 0)
1254 break;
1255 if (!n1)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001256 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
Simon Kelley39bec5f2012-01-06 22:36:58 +00001257 }
1258
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001259 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1260 {
1261 int len = option_len(opt);
1262 unsigned char *ucp = option_ptr(opt, 0);
1263 /* If the user-class option started as counted strings, the first byte will be zero. */
1264 if (len != 0 && ucp[0] == 0)
1265 ucp++, len--;
Simon Kelleyceae00d2012-02-09 21:28:14 +00001266 lease_add_extradata(lease, ucp, len, 0);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001267 }
1268 }
Simon Kelley316e2732010-01-22 20:16:09 +00001269#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001270
1271 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1272 {
1273 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001274 hostname = client_hostname;
1275 hostname_auth = 1;
1276 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001277
Simon Kelley824af852008-02-12 20:43:05 +00001278 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +00001279 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001280
Simon Kelley832af0b2007-01-21 20:01:28 +00001281 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1282 if (!hostname_auth)
1283 {
1284 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001285 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001286 break;
1287 if (id_list)
1288 hostname = NULL;
1289 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001290
1291 /* Last ditch, if configured, generate hostname from mac address */
1292 if (!hostname && emac_len != 0)
1293 {
1294 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1295 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1296 break;
1297 if (id_list)
1298 {
1299 int i;
1300
1301 hostname = daemon->dhcp_buff;
1302 /* buffer is 256 bytes, 3 bytes per octet */
1303 for (i = 0; (i < emac_len) && (i < 80); i++)
1304 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1305 hostname = daemon->dhcp_buff;
1306 }
1307 }
1308
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001309 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001310 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001311
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001312 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +00001313 lease_set_interface(lease, int_index);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001314
1315 if (override.s_addr != 0)
1316 lease->override = override;
1317 else
1318 override = lease->override;
1319
Simon Kelley7622fc02009-06-04 20:32:05 +01001320 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001321
Simon Kelley7622fc02009-06-04 20:32:05 +01001322 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001323 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001324 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001325 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001326 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001327 {
Simon Kelley59353a62004-11-21 19:34:28 +00001328 while (fuzz > (time/16))
1329 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001330 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1331 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001332 }
Simon Kelley9009d742008-11-14 20:04:27 +00001333 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001334 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001335 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001336
Simon Kelley7de060b2011-08-26 17:24:52 +01001337 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001338
1339 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001340 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001341 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001342
Simon Kelley7622fc02009-06-04 20:32:05 +01001343 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001344
Simon Kelley73a08a22009-02-05 20:28:08 +00001345 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001346 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001347
1348 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001349 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001350
Simon Kelley5aabfc72007-08-29 11:24:47 +01001351 /* Find a least based on IP address if we didn't
1352 get one from MAC address/client-d */
1353 if (!lease &&
1354 (lease = lease_find_by_addr(mess->ciaddr)) &&
1355 lease->hostname)
1356 hostname = lease->hostname;
1357
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001358 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1359 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001360
Simon Kelley73a08a22009-02-05 20:28:08 +00001361 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001362 {
1363 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001364 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001365 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001366
Simon Kelley4cb1b322012-02-06 14:30:41 +00001367 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001368
1369 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001370
Simon Kelley3927da42008-07-20 15:10:39 +01001371 if (lease)
1372 {
1373 if (override.s_addr != 0)
1374 lease->override = override;
1375 else
1376 override = lease->override;
1377 }
1378
Simon Kelley7622fc02009-06-04 20:32:05 +01001379 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001380 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001381 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1382
Simon Kelley5aabfc72007-08-29 11:24:47 +01001383 if (lease)
1384 {
1385 if (lease->expires == 0)
1386 time = 0xffffffff;
1387 else
1388 time = (unsigned int)difftime(lease->expires, now);
1389 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001390 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001391 }
Simon Kelley824af852008-02-12 20:43:05 +00001392
Simon Kelley9009d742008-11-14 20:04:27 +00001393 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001394 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001395
Simon Kelley5aabfc72007-08-29 11:24:47 +01001396 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001397 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001398 }
1399
1400 return 0;
1401}
1402
Simon Kelley6b010842007-02-12 20:32:07 +00001403/* find a good value to use as MAC address for logging and address-allocation hashing.
1404 This is normally just the chaddr field from the DHCP packet,
1405 but eg Firewire will have hlen == 0 and use the client-id instead.
1406 This could be anything, but will normally be EUI64 for Firewire.
1407 We assume that if the first byte of the client-id equals the htype byte
1408 then the client-id is using the usual encoding and use the rest of the
1409 client-id: if not we can use the whole client-id. This should give
1410 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001411unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001412 int clid_len, unsigned char *clid, int *len_out)
1413{
1414 if (hwlen == 0 && clid && clid_len > 3)
1415 {
1416 if (clid[0] == hwtype)
1417 {
1418 *len_out = clid_len - 1 ;
1419 return clid + 1;
1420 }
1421
1422#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1423 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1424 {
1425 *len_out = clid_len - 1 ;
1426 return clid + 1;
1427 }
1428#endif
1429
1430 *len_out = clid_len;
1431 return clid;
1432 }
1433
1434 *len_out = hwlen;
1435 return hwaddr;
1436}
1437
Simon Kelley824af852008-02-12 20:43:05 +00001438static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001439{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001440 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001441
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001442 if (opt)
1443 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001444 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001445 if (req_time < 120 )
1446 req_time = 120; /* sanity */
1447 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1448 time = req_time;
1449 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001450
1451 return time;
1452}
1453
Simon Kelley73a08a22009-02-05 20:28:08 +00001454static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001455{
Simon Kelley73a08a22009-02-05 20:28:08 +00001456 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001457 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001458 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001459 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001460 else
1461 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001462}
1463
Simon Kelleyf2621c72007-04-29 19:47:21 +01001464static int sanitise(unsigned char *opt, char *buf)
1465{
1466 char *p;
1467 int i;
1468
1469 *buf = 0;
1470
1471 if (!opt)
1472 return 0;
1473
Simon Kelley1a6bca82008-07-11 11:11:42 +01001474 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001475
1476 for (i = option_len(opt); i > 0; i--)
1477 {
1478 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001479 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001480 *buf++ = c;
1481 }
1482 *buf = 0; /* add terminator */
1483
1484 return 1;
1485}
1486
Simon Kelley316e2732010-01-22 20:16:09 +00001487#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001488static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1489{
1490 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001491 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001492 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001493 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001494}
1495#endif
1496
Simon Kelley5aabfc72007-08-29 11:24:47 +01001497static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001498 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001499{
Simon Kelley16972692006-10-16 20:04:18 +01001500 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001501
Simon Kelley16972692006-10-16 20:04:18 +01001502 /* addr may be misaligned */
1503 if (addr)
1504 memcpy(&a, addr, sizeof(a));
1505
Simon Kelley7622fc02009-06-04 20:32:05 +01001506 print_mac(daemon->namebuff, ext_mac, mac_len);
1507
Simon Kelley28866e92011-02-14 20:19:14 +00001508 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001509 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1510 ntohl(xid),
1511 type,
1512 interface,
1513 addr ? inet_ntoa(a) : "",
1514 addr ? " " : "",
1515 daemon->namebuff,
1516 string ? string : "");
1517 else
1518 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1519 type,
1520 interface,
1521 addr ? inet_ntoa(a) : "",
1522 addr ? " " : "",
1523 daemon->namebuff,
1524 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001525}
1526
Simon Kelley7622fc02009-06-04 20:32:05 +01001527static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001528{
1529 while (*start != OPTION_END)
1530 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001531 char *optname = option_string(AF_INET, start[0], option_ptr(start, 0), option_len(start), daemon->namebuff, MAXDNAME);
Simon Kelley7622fc02009-06-04 20:32:05 +01001532
Simon Kelley4cb1b322012-02-06 14:30:41 +00001533 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1534 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001535 start += start[1] + 2;
1536 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001537}
1538
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001539static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001540{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001541 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001542 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001543 if (p > end)
1544 return NULL;
1545 else if (*p == OPTION_END)
1546 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001547 else if (*p == OPTION_PAD)
1548 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001549 else
1550 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001551 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001552 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001553 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001554 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001555 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001556 return NULL; /* malformed packet */
1557 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001558 return p;
1559 p += opt_len + 2;
1560 }
1561 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001562}
1563
Simon Kelleycdeda282006-03-16 20:16:06 +00001564static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001565{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001566 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001567
Simon Kelley3be34542004-09-11 19:12:13 +01001568 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001569 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1570 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001571
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001572 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001573 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001574 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001575
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001576 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001577 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001578 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1579 return ret;
1580
1581 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001582 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001583 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1584 return ret;
1585
1586 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001587}
1588
Simon Kelley4cb1b322012-02-06 14:30:41 +00001589static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001590{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001591 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001592 /* struct in_addr is network byte order */
1593 struct in_addr ret;
1594
Simon Kelley4cb1b322012-02-06 14:30:41 +00001595 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001596
1597 return ret;
1598}
1599
Simon Kelley7622fc02009-06-04 20:32:05 +01001600static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001601{
1602 /* this worries about unaligned data and byte order */
1603 unsigned int ret = 0;
1604 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001605 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001606
1607 for (i = 0; i < size; i++)
1608 ret = (ret << 8) | *p++;
1609
1610 return ret;
1611}
1612
1613static unsigned char *dhcp_skip_opts(unsigned char *start)
1614{
1615 while (*start != 0)
1616 start += start[1] + 2;
1617 return start;
1618}
1619
1620/* only for use when building packet: doesn't check for bad data. */
1621static unsigned char *find_overload(struct dhcp_packet *mess)
1622{
1623 unsigned char *p = &mess->options[0] + sizeof(u32);
1624
1625 while (*p != 0)
1626 {
1627 if (*p == OPTION_OVERLOAD)
1628 return p;
1629 p += p[1] + 2;
1630 }
1631 return NULL;
1632}
1633
Simon Kelley7de060b2011-08-26 17:24:52 +01001634static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1635{
1636 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1637 unsigned char *overload;
1638 size_t ret;
1639
1640 /* move agent_id back down to the end of the packet */
1641 if (agent_id)
1642 {
1643 memmove(p, agent_id, real_end - agent_id);
1644 p += real_end - agent_id;
1645 memset(p, 0, real_end - p); /* in case of overlap */
1646 }
1647
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001648 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001649 overload = find_overload(mess);
1650
1651 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001652 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001653 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001654 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001655 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001656 }
Simon Kelley28866e92011-02-14 20:19:14 +00001657 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001658 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1659
1660 if (overload && (option_uint(overload, 0, 1) & 2))
1661 {
1662 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001663 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001664 log_options(mess->sname, mess->xid);
1665 }
Simon Kelley28866e92011-02-14 20:19:14 +00001666 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001667 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1668
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001669
1670 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001671
Simon Kelley28866e92011-02-14 20:19:14 +00001672 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001673 {
1674 if (mess->siaddr.s_addr != 0)
1675 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1676
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001677 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1678 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1679
Simon Kelley7622fc02009-06-04 20:32:05 +01001680 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1681 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001682
1683 ret = (size_t)(p - (unsigned char *)mess);
1684
1685 if (ret < MIN_PACKETSZ)
1686 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001687
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001688 return ret;
1689}
1690
1691static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1692{
1693 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1694
1695 if (p + len + 3 >= end)
1696 /* not enough space in options area, try and use overload, if poss */
1697 {
1698 unsigned char *overload;
1699
1700 if (!(overload = find_overload(mess)) &&
1701 (mess->file[0] == 0 || mess->sname[0] == 0))
1702 {
1703 /* attempt to overload fname and sname areas, we've reserved space for the
1704 overflow option previuously. */
1705 overload = p;
1706 *(p++) = OPTION_OVERLOAD;
1707 *(p++) = 1;
1708 }
1709
1710 p = NULL;
1711
1712 /* using filename field ? */
1713 if (overload)
1714 {
1715 if (mess->file[0] == 0)
1716 overload[2] |= 1;
1717
1718 if (overload[2] & 1)
1719 {
1720 p = dhcp_skip_opts(mess->file);
1721 if (p + len + 3 >= mess->file + sizeof(mess->file))
1722 p = NULL;
1723 }
1724
1725 if (!p)
1726 {
1727 /* try to bring sname into play (it may be already) */
1728 if (mess->sname[0] == 0)
1729 overload[2] |= 2;
1730
1731 if (overload[2] & 2)
1732 {
1733 p = dhcp_skip_opts(mess->sname);
1734 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1735 p = NULL;
1736 }
1737 }
1738 }
1739
1740 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001741 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 +00001742 }
1743
1744 if (p)
1745 {
1746 *(p++) = opt;
1747 *(p++) = len;
1748 }
1749
1750 return p;
1751}
1752
1753static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1754{
1755 int i;
1756 unsigned char *p = free_space(mess, end, opt, len);
1757
1758 if (p)
1759 for (i = 0; i < len; i++)
1760 *(p++) = val >> (8 * (len - (i + 1)));
1761}
1762
1763static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1764 char *string, int null_term)
1765{
1766 unsigned char *p;
1767 size_t len = strlen(string);
1768
1769 if (null_term && len != 255)
1770 len++;
1771
1772 if ((p = free_space(mess, end, opt, len)))
1773 memcpy(p, string, len);
1774}
1775
1776/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001777static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001778{
1779 int len = opt->len;
1780
1781 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1782 len++;
1783
1784 if (p && len != 0)
1785 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001786 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001787 {
1788 int j;
1789 struct in_addr *a = (struct in_addr *)opt->val;
1790 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1791 {
1792 /* zero means "self" (but not in vendorclass options.) */
1793 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001794 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001795 else
1796 memcpy(p, a, INADDRSZ);
1797 p += INADDRSZ;
1798 }
1799 }
1800 else
1801 memcpy(p, opt->val, len);
1802 }
1803 return len;
1804}
Simon Kelley7622fc02009-06-04 20:32:05 +01001805
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001806static int in_list(unsigned char *list, int opt)
1807{
1808 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001809
1810 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001811 if (!list)
1812 return 1;
1813
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001814 for (i = 0; list[i] != OPTION_END; i++)
1815 if (opt == list[i])
1816 return 1;
1817
1818 return 0;
1819}
1820
Simon Kelley7de060b2011-08-26 17:24:52 +01001821static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001822{
Simon Kelley7de060b2011-08-26 17:24:52 +01001823 struct dhcp_opt *opts;
1824
1825 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1826 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1827 return opts;
1828
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001829 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001830}
1831
Simon Kelley6b010842007-02-12 20:32:07 +00001832/* mark vendor-encapsulated options which match the client-supplied or
1833 config-supplied vendor class */
1834static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1835{
1836 for (; dopt; dopt = dopt->next)
1837 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001838 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001839 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001840 {
1841 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001842 if (dopt->u.vendor_class)
1843 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001844 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001845 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001846 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001847 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001848 break;
1849 }
1850 }
1851 }
1852}
1853
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001854static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1855 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001856{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001857 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001858 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001859 unsigned char *p;
1860
1861 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001862 for (enc_len = 0, start = opt; opt; opt = opt->next)
1863 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001864 {
1865 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001866 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001867 if (enc_len + new <= 255)
1868 enc_len += new;
1869 else
1870 {
1871 p = free_space(mess, end, encap, enc_len);
1872 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001873 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001874 {
1875 len = do_opt(start, p + 2, NULL, null_term);
1876 *(p++) = start->opt;
1877 *(p++) = len;
1878 p += len;
1879 }
1880 enc_len = new;
1881 start = opt;
1882 }
1883 }
1884
1885 if (enc_len != 0 &&
1886 (p = free_space(mess, end, encap, enc_len + 1)))
1887 {
1888 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001889 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001890 {
1891 len = do_opt(start, p + 2, NULL, null_term);
1892 *(p++) = start->opt;
1893 *(p++) = len;
1894 p += len;
1895 }
1896 *p = OPTION_END;
1897 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001898
1899 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00001900}
1901
Simon Kelley7622fc02009-06-04 20:32:05 +01001902static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01001903{
Simon Kelley7622fc02009-06-04 20:32:05 +01001904 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01001905
Simon Kelley7622fc02009-06-04 20:32:05 +01001906 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1907 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1908 memcpy(p, uuid, 17);
1909}
1910
1911static int prune_vendor_opts(struct dhcp_netid *netid)
1912{
1913 int force = 0;
1914 struct dhcp_opt *opt;
1915
1916 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1917 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1918 if (opt->flags & DHOPT_VENDOR_MATCH)
1919 {
1920 if (!match_netid(opt->netid, netid, 1))
1921 opt->flags &= ~DHOPT_VENDOR_MATCH;
1922 else if (opt->flags & DHOPT_FORCE)
1923 force = 1;
1924 }
1925 return force;
1926}
1927
Simon Kelley751d6f42012-02-10 15:24:51 +00001928static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01001929{
1930#define NUM_OPTS 4
1931
1932 unsigned char *p, *q;
1933 struct pxe_service *service;
1934 static struct dhcp_opt *o, *ret;
1935 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001936 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01001937
1938 /* We pass back references to these, hence they are declared static */
1939 static unsigned char discovery_control;
1940 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1941 static struct dhcp_opt *fake_opts = NULL;
1942
Simon Kelley316e2732010-01-22 20:16:09 +00001943 /* Disable multicast, since we don't support it, and broadcast
1944 unless we need it */
1945 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001946
1947 ret = daemon->dhcp_opts;
1948
1949 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
1950 return ret;
1951
1952 for (i = 0; i < NUM_OPTS; i++)
1953 {
1954 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
1955 fake_opts[i].netid = NULL;
1956 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
1957 }
1958
1959 /* create the data for the PXE_MENU and PXE_SERVERS options. */
1960 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001961 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001962
1963 for (i = 0, service = daemon->pxe_services; service; service = service->next)
1964 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
1965 {
1966 size_t len = strlen(service->menu);
1967 /* opt 43 max size is 255. encapsulated option has type and length
1968 bytes, so its max size is 253. */
1969 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
1970 {
1971 *(p++) = service->type >> 8;
1972 *(p++) = service->type;
1973 *(p++) = len;
1974 memcpy(p, service->menu, len);
1975 p += len;
1976 i++;
1977 }
1978 else
1979 {
1980 toobig:
1981 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
1982 return daemon->dhcp_opts;
1983 }
1984
Simon Kelley751d6f42012-02-10 15:24:51 +00001985 boot_server = service->basename ? local :
1986 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
1987
Simon Kelley316e2732010-01-22 20:16:09 +00001988 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001989 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001990 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00001991 goto toobig;
1992
1993 /* Boot service with known address - give it */
1994 *(q++) = service->type >> 8;
1995 *(q++) = service->type;
1996 *(q++) = 1;
1997 /* dest misaligned */
1998 memcpy(q, &boot_server.s_addr, INADDRSZ);
1999 q += INADDRSZ;
2000 }
2001 else if (service->type != 0)
2002 /* We don't know the server for a service type, so we'll
2003 allow the client to broadcast for it */
2004 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002005 }
2006
2007 /* if no prompt, wait forever if there's a choice */
2008 fake_prompt[0] = (i > 1) ? 255 : 0;
2009
2010 if (i == 0)
2011 discovery_control = 8; /* no menu - just use use mess->filename */
2012 else
2013 {
2014 ret = &fake_opts[j--];
2015 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2016 ret->val = (unsigned char *)daemon->dhcp_buff;
2017 ret->opt = SUBOPT_PXE_MENU;
2018
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002019 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002020 {
2021 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002022 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2023 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002024 ret->opt = SUBOPT_PXE_SERVERS;
2025 }
2026 }
2027
2028 for (o = daemon->dhcp_opts; o; o = o->next)
2029 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2030 break;
2031
2032 if (!o)
2033 {
2034 ret = &fake_opts[j--];
2035 ret->len = sizeof(fake_prompt);
2036 ret->val = fake_prompt;
2037 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2038 }
2039
Simon Kelley316e2732010-01-22 20:16:09 +00002040 ret = &fake_opts[j--];
2041 ret->len = 1;
2042 ret->opt = SUBOPT_PXE_DISCOVERY;
2043 ret->val= &discovery_control;
2044
Simon Kelley7622fc02009-06-04 20:32:05 +01002045 return ret;
2046}
2047
2048static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2049{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002050 memset(mess->sname, 0, sizeof(mess->sname));
2051 memset(mess->file, 0, sizeof(mess->file));
2052 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2053 mess->siaddr.s_addr = 0;
2054}
Simon Kelleycdeda282006-03-16 20:16:06 +00002055
Simon Kelley7622fc02009-06-04 20:32:05 +01002056struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002057{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002058 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002059
2060 /* decide which dhcp-boot option we're using */
2061 for (boot = daemon->boot_config; boot; boot = boot->next)
2062 if (match_netid(boot->netid, netid, 0))
2063 break;
2064 if (!boot)
2065 /* No match, look for one without a netid */
2066 for (boot = daemon->boot_config; boot; boot = boot->next)
2067 if (match_netid(boot->netid, netid, 1))
2068 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002069
2070 return boot;
2071}
2072
2073static void do_options(struct dhcp_context *context,
2074 struct dhcp_packet *mess,
2075 unsigned char *end,
2076 unsigned char *req_options,
2077 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002078 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002079 struct dhcp_netid *netid,
2080 struct in_addr subnet_addr,
2081 unsigned char fqdn_flags,
2082 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002083 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002084 int vendor_class_len,
2085 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002086{
2087 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2088 struct dhcp_boot *boot;
2089 unsigned char *p;
2090 int i, len, force_encap = 0;
2091 unsigned char f0 = 0, s0 = 0;
2092 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002093 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002094 struct dhcp_netid *tagif;
2095 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002096
Simon Kelley4cb1b322012-02-06 14:30:41 +00002097 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley6caacac2012-02-15 21:58:33 +00002098 context->netid.next = NULL;
2099 tagif = option_filter(netid, context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002100
Simon Kelley7622fc02009-06-04 20:32:05 +01002101 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002102 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002103 {
2104 char *q = daemon->namebuff;
2105 for (i = 0; req_options[i] != OPTION_END; i++)
2106 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002107 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002108 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2109 "%d%s%s%s",
2110 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002111 strlen(s) != 0 ? ":" : "",
2112 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002113 req_options[i+1] == OPTION_END ? "" : ", ");
2114 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2115 {
2116 q = daemon->namebuff;
2117 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2118 }
2119 }
2120 }
2121
Simon Kelley7de060b2011-08-26 17:24:52 +01002122 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2123 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2124 break;
2125 if (id_list)
2126 mess->flags |= htons(0x8000); /* force broadcast */
2127
Simon Kelley73a08a22009-02-05 20:28:08 +00002128 if (context)
2129 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002130
2131 /* See if we can send the boot stuff as options.
2132 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002133 and very old DHCP clients won't have this, we also
2134 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002135 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002136 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002137 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002138 {
2139 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002140 {
Simon Kelley28866e92011-02-14 20:19:14 +00002141 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002142 req_options &&
2143 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002144 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2145 else
Simon Kelley824af852008-02-12 20:43:05 +00002146 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002147 }
2148
2149 if (boot->file)
2150 {
Simon Kelley28866e92011-02-14 20:19:14 +00002151 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002152 req_options &&
2153 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002154 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2155 else
Simon Kelley824af852008-02-12 20:43:05 +00002156 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002157 }
2158
Simon Kelley7de060b2011-08-26 17:24:52 +01002159 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002160 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002161 else if (boot->tftp_sname)
2162 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002163 }
Simon Kelley824af852008-02-12 20:43:05 +00002164 else
2165 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002166 they're not explicitly asked for as options. OPTION_END is used
2167 as an internal way to specify siaddr without using dhcp-boot, for use in
2168 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002169 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002170 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002171 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002172 {
2173 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2174 done_file = 1;
2175 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002176
Simon Kelley824af852008-02-12 20:43:05 +00002177 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002178 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002179 {
2180 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2181 done_server = 1;
2182 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002183
Simon Kelley7de060b2011-08-26 17:24:52 +01002184 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002185 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002186 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002187
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002188 /* We don't want to do option-overload for BOOTP, so make the file and sname
2189 fields look like they are in use, even when they aren't. This gets restored
2190 at the end of this function. */
2191
Simon Kelley28866e92011-02-14 20:19:14 +00002192 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002193 {
2194 f0 = mess->file[0];
2195 mess->file[0] = 1;
2196 s0 = mess->sname[0];
2197 mess->sname[0] = 1;
2198 }
2199
2200 /* At this point, if mess->sname or mess->file are zeroed, they are available
2201 for option overload, reserve space for the overload option. */
2202 if (mess->file[0] == 0 || mess->sname[0] == 0)
2203 end -= 3;
2204
Simon Kelley3be34542004-09-11 19:12:13 +01002205 /* rfc3011 says this doesn't need to be in the requested options list. */
2206 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002207 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002208
2209 /* replies to DHCPINFORM may not have a valid context */
2210 if (context)
2211 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002212 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002213 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2214
2215 /* May not have a "guessed" broadcast address if we got no packets via a relay
2216 from this net yet (ie just unicast renewals after a restart */
2217 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002218 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002219 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2220
2221 /* Same comments as broadcast apply, and also may not be able to get a sensible
2222 default when using subnet select. User must configure by steam in that case. */
2223 if (context->router.s_addr &&
2224 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002225 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002226 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2227
2228 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002229 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002230 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2231 }
Simon Kelley3be34542004-09-11 19:12:13 +01002232
Simon Kelley9009d742008-11-14 20:04:27 +00002233 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002234 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002235 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002236
Simon Kelley824af852008-02-12 20:43:05 +00002237 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002238 if (hostname)
2239 {
Simon Kelley824af852008-02-12 20:43:05 +00002240 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002241 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002242 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002243
2244 if (fqdn_flags != 0)
2245 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002246 len = strlen(hostname) + 3;
2247
Simon Kelley3d8df262005-08-29 12:19:27 +01002248 if (fqdn_flags & 0x04)
2249 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002250 else if (null_term)
2251 len++;
2252
Simon Kelley9009d742008-11-14 20:04:27 +00002253 if (domain)
2254 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002255
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002256 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002257 {
Simon Kelley3d8df262005-08-29 12:19:27 +01002258 *(p++) = fqdn_flags;
2259 *(p++) = 255;
2260 *(p++) = 255;
2261
2262 if (fqdn_flags & 0x04)
2263 {
2264 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002265 if (domain)
2266 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002267 *p++ = 0;
2268 }
2269 else
2270 {
2271 memcpy(p, hostname, strlen(hostname));
2272 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002273 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002274 {
2275 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002276 memcpy(p, domain, strlen(domain));
2277 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002278 }
2279 if (null_term)
2280 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002281 }
2282 }
2283 }
2284 }
2285
Simon Kelley6b010842007-02-12 20:32:07 +00002286 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002287 {
Simon Kelley824af852008-02-12 20:43:05 +00002288 int optno = opt->opt;
2289
Simon Kelley7de060b2011-08-26 17:24:52 +01002290 /* netids match and not encapsulated? */
2291 if (!(opt->flags & DHOPT_TAGOK))
2292 continue;
2293
Simon Kelley6b010842007-02-12 20:32:07 +00002294 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002295 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002296 continue;
2297
2298 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002299 if (optno == OPTION_CLIENT_FQDN ||
2300 optno == OPTION_MAXMESSAGE ||
2301 optno == OPTION_OVERLOAD ||
2302 optno == OPTION_PAD ||
2303 optno == OPTION_END)
2304 continue;
2305
2306 if (optno == OPTION_SNAME && done_server)
2307 continue;
2308
2309 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002310 continue;
2311
Simon Kelley33820b72004-04-03 21:10:00 +01002312 /* For the options we have default values on
2313 dhc-option=<optionno> means "don't include this option"
2314 not "include a zero-length option" */
2315 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002316 (optno == OPTION_NETMASK ||
2317 optno == OPTION_BROADCAST ||
2318 optno == OPTION_ROUTER ||
2319 optno == OPTION_DNSSERVER ||
2320 optno == OPTION_DOMAINNAME ||
2321 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002322 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002323
2324 /* vendor-class comes from elsewhere for PXE */
2325 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2326 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002327
Simon Kelley7622fc02009-06-04 20:32:05 +01002328 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002329 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002330 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002331
Simon Kelley824af852008-02-12 20:43:05 +00002332 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002333 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002334 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002335 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2336
Simon Kelley6b010842007-02-12 20:32:07 +00002337 /* If we send a vendor-id, revisit which vendor-ops we consider
2338 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002339 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002340 {
2341 match_vendor_opts(p - 2, config_opts);
2342 done_vendor_class = 1;
2343 }
Simon Kelley6b010842007-02-12 20:32:07 +00002344 }
2345 }
2346
Simon Kelley73a08a22009-02-05 20:28:08 +00002347 /* Now send options to be encapsulated in arbitrary options,
2348 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002349 Also handle vendor-identifying vendor-encapsulated options,
2350 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002351 The may be more that one "outer" to do, so group
2352 all the options which match each outer in turn. */
2353 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002354 opt->flags &= ~DHOPT_ENCAP_DONE;
2355
2356 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002357 {
2358 int flags;
2359
2360 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2361 {
2362 int found = 0;
2363 struct dhcp_opt *o;
2364
2365 if (opt->flags & DHOPT_ENCAP_DONE)
2366 continue;
2367
2368 for (len = 0, o = config_opts; o; o = o->next)
2369 {
2370 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2371
2372 o->flags &= ~DHOPT_ENCAP_MATCH;
2373
2374 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2375 continue;
2376
2377 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002378 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002379 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2380 {
2381 o->flags |= DHOPT_ENCAP_MATCH;
2382 found = 1;
2383 len += do_opt(o, NULL, NULL, 0) + 2;
2384 }
2385 }
2386
2387 if (found)
2388 {
2389 if (flags & DHOPT_ENCAPSULATE)
2390 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2391 else if (len > 250)
2392 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2393 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2394 {
2395 int swap_ent = htonl(opt->u.encap);
2396 memcpy(p, &swap_ent, 4);
2397 p += 4;
2398 *(p++) = len;
2399 for (o = config_opts; o; o = o->next)
2400 if (o->flags & DHOPT_ENCAP_MATCH)
2401 {
2402 len = do_opt(o, p + 2, NULL, 0);
2403 *(p++) = o->opt;
2404 *(p++) = len;
2405 p += len;
2406 }
2407 }
2408 }
2409 }
2410 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002411
Simon Kelley7de060b2011-08-26 17:24:52 +01002412 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002413
Simon Kelley316e2732010-01-22 20:16:09 +00002414 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002415 {
2416 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002417 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002418 }
2419
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002420 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2421 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2422 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2423 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2424 /* If we send vendor encapsulated options, and haven't already sent option 60,
2425 echo back the value we got from the client. */
2426 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2427
Simon Kelley7622fc02009-06-04 20:32:05 +01002428 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002429 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002430 {
2431 mess->file[0] = f0;
2432 mess->sname[0] = s0;
2433 }
2434}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002435
Simon Kelley7622fc02009-06-04 20:32:05 +01002436#endif
2437
2438
2439
2440
2441
2442
2443