blob: 250b1abc2f87ee6d4df7e5a3e47836527ece0990 [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 Kelleya9ab7322012-04-28 11:29:37 +0100486 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0, now, 1);
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 Kelley353ae4d2012-03-19 20:07:51 +0000493 lease_set_interface(lease, int_index, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100494
Simon Kelley7622fc02009-06-04 20:32:05 +0100495 clear_packet(mess, end);
Simon Kelley4d0f5b42012-09-05 23:29:30 +0100496 match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */
Simon Kelley9009d742008-11-14 20:04:27 +0000497 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
Simon Kelley4d0f5b42012-09-05 23:29:30 +0100498 netid, subnet_addr, 0, 0, -1, NULL, 0, now);
Simon Kelley3d8df262005-08-29 12:19:27 +0100499 }
500 }
501
Simon Kelley7622fc02009-06-04 20:32:05 +0100502 log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000503
Simon Kelley7de060b2011-08-26 17:24:52 +0100504 return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley3be34542004-09-11 19:12:13 +0100505 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000506
Simon Kelley3d8df262005-08-29 12:19:27 +0100507 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
508 {
509 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
510 int len = option_len(opt);
511 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100512 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100513
Simon Kelley9fed0f72012-08-30 11:43:35 +0100514 fqdn_flags = *op;
Simon Kelley3d8df262005-08-29 12:19:27 +0100515 len -= 3;
516 op += 3;
517 pp = op;
518
Simon Kelley9fed0f72012-08-30 11:43:35 +0100519 /* NB, the following always sets at least one bit */
520 if (option_bool(OPT_FQDN_UPDATE))
521 {
522 if (fqdn_flags & 0x01)
523 {
524 fqdn_flags |= 0x02; /* set O */
525 fqdn_flags &= ~0x01; /* clear S */
526 }
527 fqdn_flags |= 0x08; /* set N */
528 }
529 else
530 {
531 if (!(fqdn_flags & 0x01))
532 fqdn_flags |= 0x03; /* set S and O */
533 fqdn_flags &= ~0x08; /* clear N */
534 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100535
536 if (fqdn_flags & 0x04)
537 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
538 {
539 memcpy(pq, op+1, *op);
540 pq += *op;
541 op += (*op)+1;
542 *(pq++) = '.';
543 }
544 else
545 {
546 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000547 if (len > 0 && op[len-1] == 0)
548 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100549 pq += len + 1;
550 }
551
552 if (pq != daemon->dhcp_buff)
553 pq--;
554
555 *pq = 0;
556
Simon Kelley1f15b812009-10-13 17:49:32 +0100557 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100558 offer_hostname = client_hostname = daemon->dhcp_buff;
559 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000560 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000562 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100563 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000564 /* Microsoft clients are broken, and need zero-terminated strings
565 in options. We detect this state here, and do the same in
566 any options we send */
567 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
568 borken_opt = 1;
569 else
570 daemon->dhcp_buff[len] = 0;
Simon Kelley1f15b812009-10-13 17:49:32 +0100571 if (legal_hostname(daemon->dhcp_buff))
Simon Kelley3d8df262005-08-29 12:19:27 +0100572 client_hostname = daemon->dhcp_buff;
573 }
574
Simon Kelley28866e92011-02-14 20:19:14 +0000575 if (client_hostname && option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +0100576 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
577
Simon Kelley3d8df262005-08-29 12:19:27 +0100578 if (have_config(config, CONFIG_NAME))
579 {
580 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000581 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000582 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000583 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100584 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000585 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100586 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100587 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100588 {
Simon Kelley9009d742008-11-14 20:04:27 +0000589 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100590
591 if (strlen(client_hostname) != 0)
592 {
593 hostname = client_hostname;
594 if (!config)
595 {
596 /* Search again now we have a hostname.
597 Only accept configs without CLID and HWADDR here, (they won't match)
598 to avoid impersonation by name. */
599 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
600 mess->chaddr, mess->hlen,
601 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000602 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000603 {
604 config = new;
605 /* set "known" tag for known hosts */
606 known_id.net = "known";
607 known_id.next = netid;
608 netid = &known_id;
609 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100610 }
611 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000612 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100613
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100614 if (config)
Simon Kelleya2226412004-05-13 20:27:08 +0100615 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100616 struct dhcp_netid_list *list;
617
618 for (list = config->netid; list; list = list->next)
619 {
620 list->list->next = netid;
621 netid = list->list;
622 }
Simon Kelleya2226412004-05-13 20:27:08 +0100623 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100624
Simon Kelley73a08a22009-02-05 20:28:08 +0000625 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
626 Otherwise assume the option is an array, and look for a matching element.
Simon Kelley316e2732010-01-22 20:16:09 +0000627 If no data given, existance of the option is enough. This code handles
628 rfc3925 V-I classes too. */
Simon Kelley73a08a22009-02-05 20:28:08 +0000629 for (o = daemon->dhcp_match; o; o = o->next)
630 {
Simon Kelley316e2732010-01-22 20:16:09 +0000631 unsigned int len, elen, match = 0;
632 size_t offset, o2;
Simon Kelley73a08a22009-02-05 20:28:08 +0000633
Simon Kelley316e2732010-01-22 20:16:09 +0000634 if (o->flags & DHOPT_RFC3925)
635 {
636 if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
637 continue;
638
639 for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
640 {
641 len = option_uint(opt, offset + 4 , 1);
642 /* Need to take care that bad data can't run us off the end of the packet */
643 if ((offset + len + 5 <= (option_len(opt))) &&
644 (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
645 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
646 {
647 elen = option_uint(opt, o2, 1);
648 if ((o2 + elen + 1 <= option_len(opt)) &&
649 (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
650 break;
651 }
652 if (match)
653 break;
654 }
655 }
656 else
657 {
658 if (!(opt = option_find(mess, sz, o->opt, 1)))
659 continue;
660
661 match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
662 }
663
664 if (match)
Simon Kelley73a08a22009-02-05 20:28:08 +0000665 {
666 o->netid->next = netid;
667 netid = o->netid;
668 }
669 }
670
Simon Kelley26128d22004-11-14 16:43:54 +0000671 /* user-class options are, according to RFC3004, supposed to contain
672 a set of counted strings. Here we check that this is so (by seeing
673 if the counts are consistent with the overall option length) and if
674 so zero the counts so that we don't get spurious matches between
675 the vendor string and the counts. If the lengths don't add up, we
676 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100677 and just do the substring match. dhclient provides these broken options.
678 The code, later, which sends user-class data to the lease-change script
679 relies on the transformation done here.
680 */
Simon Kelleya2226412004-05-13 20:27:08 +0100681
Simon Kelleybb01cb92004-12-13 20:56:23 +0000682 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100683 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100684 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000685 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000686 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
687 if (j == option_len(opt))
688 for (j = 0; j < option_len(opt); j = tmp)
689 {
690 tmp = j + ucp[j] + 1;
691 ucp[j] = 0;
692 }
Simon Kelleya2226412004-05-13 20:27:08 +0100693 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000694
Simon Kelley26128d22004-11-14 16:43:54 +0000695 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100696 {
697 int mopt;
698
699 if (vendor->match_type == MATCH_VENDOR)
700 mopt = OPTION_VENDOR_ID;
701 else if (vendor->match_type == MATCH_USER)
702 mopt = OPTION_USER_CLASS;
703 else
704 continue;
705
706 if ((opt = option_find(mess, sz, mopt, 1)))
707 {
708 int i;
709 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100710 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100711 {
712 vendor->netid.next = netid;
713 netid = &vendor->netid;
714 break;
715 }
716 }
717 }
718
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100719 /* mark vendor-encapsulated options which match the client-supplied vendor class,
720 save client-supplied vendor class */
721 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
722 {
723 memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
724 vendor_class_len = option_len(opt);
725 }
726 match_vendor_opts(opt, daemon->dhcp_opts);
727
Simon Kelley28866e92011-02-14 20:19:14 +0000728 if (option_bool(OPT_LOG_OPTS))
Simon Kelleyf2621c72007-04-29 19:47:21 +0100729 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100730 if (sanitise(opt, daemon->namebuff))
731 my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100732 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100733 my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100734 }
735
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100736 tagif_netid = run_tag_if(netid);
737
Simon Kelley26128d22004-11-14 16:43:54 +0000738 /* if all the netids in the ignore list are present, ignore this client */
739 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100740 if (match_netid(id_list->list, tagif_netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000741 ignore = 1;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100742
743 /* If configured, we can override the server-id to be the address of the relay,
744 so that all traffic goes via the relay and can pick up agent-id info. This can be
745 configured for all relays, or by address. */
746 if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
747 {
748 if (!daemon->override_relays)
749 override = mess->giaddr;
750 else
751 {
752 struct addr_list *l;
753 for (l = daemon->override_relays; l; l = l->next)
754 if (l->addr.s_addr == mess->giaddr.s_addr)
755 break;
756 if (l)
757 override = mess->giaddr;
758 }
759 }
760
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100761 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
762 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000763 clid = NULL;
764
Simon Kelley7622fc02009-06-04 20:32:05 +0100765 /* Check if client is PXE client. */
Simon Kelley1f15b812009-10-13 17:49:32 +0100766 if (daemon->enable_pxe &&
767 (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100768 strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
769 {
770 if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
771 {
772 memcpy(pxe_uuid, option_ptr(opt, 0), 17);
773 uuid = pxe_uuid;
774 }
775
776 /* Check if this is really a PXE bootserver request, and handle specially if so. */
777 if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
778 (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
779 (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
780 {
781 struct pxe_service *service;
782 int type = option_uint(opt, 0, 2);
783 int layer = option_uint(opt, 2, 2);
784 unsigned char save71[4];
785 struct dhcp_opt opt71;
786
Simon Kelley1f15b812009-10-13 17:49:32 +0100787 if (ignore)
788 return 0;
789
Simon Kelley7622fc02009-06-04 20:32:05 +0100790 if (layer & 0x8000)
791 {
792 my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
793 return 0;
794 }
795
796 memcpy(save71, option_ptr(opt, 0), 4);
797
798 for (service = daemon->pxe_services; service; service = service->next)
799 if (service->type == type)
800 break;
801
802 if (!service || !service->basename)
803 return 0;
804
805 clear_packet(mess, end);
806
807 mess->yiaddr = mess->ciaddr;
808 mess->ciaddr.s_addr = 0;
Simon Kelley751d6f42012-02-10 15:24:51 +0000809 if (service->sname)
810 mess->siaddr = a_record_from_hosts(service->sname, now);
811 else if (service->server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +0100812 mess->siaddr = service->server;
813 else
814 mess->siaddr = context->local;
815
816 snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
817 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
818 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
819 pxe_misc(mess, end, uuid);
820
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100821 prune_vendor_opts(tagif_netid);
Simon Kelley7622fc02009-06-04 20:32:05 +0100822 opt71.val = save71;
823 opt71.opt = SUBOPT_PXE_BOOT_ITEM;
824 opt71.len = 4;
825 opt71.flags = DHOPT_VENDOR_MATCH;
826 opt71.netid = NULL;
827 opt71.next = daemon->dhcp_opts;
828 do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
829
830 log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000831 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100832 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100833 }
834
835 if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
836 {
837 pxearch = option_uint(opt, 0, 2);
838
Simon Kelley316e2732010-01-22 20:16:09 +0000839 /* proxy DHCP here. */
Simon Kelley28866e92011-02-14 20:19:14 +0000840 if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
Simon Kelley7622fc02009-06-04 20:32:05 +0100841 {
Simon Kelley28866e92011-02-14 20:19:14 +0000842 struct dhcp_context *tmp;
Simon Kelley7622fc02009-06-04 20:32:05 +0100843
Simon Kelley28866e92011-02-14 20:19:14 +0000844 for (tmp = context; tmp; tmp = tmp->current)
845 if ((tmp->flags & CONTEXT_PROXY) &&
846 match_netid(tmp->filter, tagif_netid, 1))
847 break;
848
849 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100850 {
Simon Kelley28866e92011-02-14 20:19:14 +0000851 struct dhcp_boot *boot = find_boot(tagif_netid);
852
853 mess->yiaddr.s_addr = 0;
854 if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
855 {
856 mess->ciaddr.s_addr = 0;
857 mess->flags |= htons(0x8000); /* broadcast */
858 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100859
Simon Kelley28866e92011-02-14 20:19:14 +0000860 clear_packet(mess, end);
861
862 /* Provide the bootfile here, for gPXE, and in case we have no menu items
863 and set discovery_control = 8 */
864 if (boot)
865 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100866 if (boot->next_server.s_addr)
Simon Kelley28866e92011-02-14 20:19:14 +0000867 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100868 else if (boot->tftp_sname)
869 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley28866e92011-02-14 20:19:14 +0000870
871 if (boot->file)
872 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
873 }
874
875 option_put(mess, end, OPTION_MESSAGE_TYPE, 1,
876 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
877 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
878 pxe_misc(mess, end, uuid);
879 prune_vendor_opts(tagif_netid);
Simon Kelley751d6f42012-02-10 15:24:51 +0000880 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 +0000881
882 log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
Simon Kelley4cb1b322012-02-06 14:30:41 +0000883 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +0100884 return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley7622fc02009-06-04 20:32:05 +0100885 }
Simon Kelley7622fc02009-06-04 20:32:05 +0100886 }
887 }
888 }
889
890 /* if we're just a proxy server, go no further */
Simon Kelley316e2732010-01-22 20:16:09 +0000891 if ((context->flags & CONTEXT_PROXY) || pxe)
Simon Kelley7622fc02009-06-04 20:32:05 +0100892 return 0;
893
Simon Kelleybb01cb92004-12-13 20:56:23 +0000894 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100895 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100896 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100897 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000898 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100899 }
900
Simon Kelley3be34542004-09-11 19:12:13 +0100901 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000902 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000903 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000904 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000905 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000906 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100907
Simon Kelley44a2a312004-03-10 20:04:35 +0000908 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100909 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000910
Simon Kelleybb01cb92004-12-13 20:56:23 +0000911 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000912 return 0;
913
Simon Kelley7622fc02009-06-04 20:32:05 +0100914 log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000915
916 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
917 lease_prune(lease, now);
918
Simon Kelley33820b72004-04-03 21:10:00 +0100919 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000920 config->addr.s_addr == option_addr(opt).s_addr)
921 {
Simon Kelley849a8352006-06-09 21:02:31 +0100922 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelley7622fc02009-06-04 20:32:05 +0100923 my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
Simon Kelleyf2621c72007-04-29 19:47:21 +0100924 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100925 config->flags |= CONFIG_DECLINED;
926 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000927 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100928 else
929 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100930 for (; context; context = context->current)
931 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000932
933 return 0;
934
935 case DHCPRELEASE:
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100936 if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100937 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000938 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000939 return 0;
940
Simon Kelley44a2a312004-03-10 20:04:35 +0000941 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
942 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100943 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000944 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100945
Simon Kelley7622fc02009-06-04 20:32:05 +0100946 log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +0000947
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000948 return 0;
949
950 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100951 if (ignore || have_config(config, CONFIG_DISABLE))
952 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000953 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100954 opt = NULL;
955 }
956 else
957 {
958 struct in_addr addr, conf;
959
Simon Kelley1a6bca82008-07-11 11:11:42 +0100960 addr.s_addr = conf.s_addr = 0;
961
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100962 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
963 addr = option_addr(opt);
964
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100965 if (have_config(config, CONFIG_ADDR))
966 {
Simon Kelley849a8352006-06-09 21:02:31 +0100967 char *addrs = inet_ntoa(config->addr);
968
Simon Kelley9009d742008-11-14 20:04:27 +0000969 if ((ltmp = lease_find_by_addr(config->addr)) &&
970 ltmp != lease &&
971 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000972 {
973 int len;
974 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
975 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelley7622fc02009-06-04 20:32:05 +0100976 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100977 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000978 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100979 else
980 {
981 struct dhcp_context *tmp;
982 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100983 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100984 break;
985 if (tmp)
Simon Kelley7622fc02009-06-04 20:32:05 +0100986 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 +0100987 else if (have_config(config, CONFIG_DECLINED) &&
988 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelley7622fc02009-06-04 20:32:05 +0100989 my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100990 else
991 conf = config->addr;
992 }
993 }
994
995 if (conf.s_addr)
996 mess->yiaddr = conf;
Simon Kelley7622fc02009-06-04 20:32:05 +0100997 else if (lease &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100998 address_available(context, lease->addr, tagif_netid) &&
Simon Kelley7622fc02009-06-04 20:32:05 +0100999 !config_find_by_address(daemon->dhcp_conf, lease->addr))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001000 mess->yiaddr = lease->addr;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001001 else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001002 !config_find_by_address(daemon->dhcp_conf, addr))
1003 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001004 else if (emac_len == 0)
1005 message = _("no unique-id");
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001006 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001007 message = _("no address available");
1008 }
1009
Simon Kelley7622fc02009-06-04 20:32:05 +01001010 log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley3d8df262005-08-29 12:19:27 +01001011
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001012 if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelley33820b72004-04-03 21:10:00 +01001013 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +00001014
Simon Kelleycdeda282006-03-16 20:16:06 +00001015 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001016 {
1017 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001018 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001019 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001020
Simon Kelley4cb1b322012-02-06 14:30:41 +00001021 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001022
1023 log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
1024
Simon Kelley824af852008-02-12 20:43:05 +00001025 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley7622fc02009-06-04 20:32:05 +01001026 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001027 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +00001028 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001029 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001030 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +00001031 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001032 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001033 option_put(mess, end, OPTION_T1, 4, (time/2));
1034 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001035 }
Simon Kelley9009d742008-11-14 20:04:27 +00001036 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001037 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001038
Simon Kelley7de060b2011-08-26 17:24:52 +01001039 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001040
1041 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +00001042 if (ignore || have_config(config, CONFIG_DISABLE))
1043 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001044 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001045 {
1046 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001047 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +00001048
Simon Kelley4011c4e2006-10-28 16:26:19 +01001049 /* send vendor and user class info for new or recreated lease */
1050 do_classes = 1;
1051
Simon Kelleybb01cb92004-12-13 20:56:23 +00001052 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001053 {
Simon Kelley3be34542004-09-11 19:12:13 +01001054 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +00001055 selecting = 1;
1056
Simon Kelley1a6bca82008-07-11 11:11:42 +01001057 if (override.s_addr != 0)
1058 {
1059 if (option_addr(opt).s_addr != override.s_addr)
1060 return 0;
1061 }
Simon Kelley9009d742008-11-14 20:04:27 +00001062 else
Simon Kelley1a6bca82008-07-11 11:11:42 +01001063 {
1064 for (; context; context = context->current)
1065 if (context->local.s_addr == option_addr(opt).s_addr)
1066 break;
1067
1068 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +00001069 {
Simon Kelley7de060b2011-08-26 17:24:52 +01001070 /* Handle very strange configs where clients have more than one route to the server.
1071 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
1072 Have to set override to make sure we echo back the correct server-id */
1073 struct irec *intr;
1074
1075 enumerate_interfaces();
1076
1077 for (intr = daemon->interfaces; intr; intr = intr->next)
1078 if (intr->addr.sa.sa_family == AF_INET &&
1079 intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
1080 intr->tftp_ok)
1081 break;
1082
1083 if (intr)
1084 override = intr->addr.in.sin_addr;
1085 else
1086 {
1087 /* In auth mode, a REQUEST sent to the wrong server
1088 should be faulted, so that the client establishes
1089 communication with us, otherwise, silently ignore. */
1090 if (!option_bool(OPT_AUTHORITATIVE))
1091 return 0;
1092 message = _("wrong server-ID");
1093 }
Simon Kelley9009d742008-11-14 20:04:27 +00001094 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001095 }
Simon Kelleye17fb622006-01-14 20:33:46 +00001096
Simon Kelley3be34542004-09-11 19:12:13 +01001097 /* If a lease exists for this host and another address, squash it. */
1098 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
1099 {
1100 lease_prune(lease, now);
1101 lease = NULL;
1102 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001103 }
Simon Kelley3be34542004-09-11 19:12:13 +01001104 else
1105 {
1106 /* INIT-REBOOT */
Simon Kelley28866e92011-02-14 20:19:14 +00001107 if (!lease && !option_bool(OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +01001108 return 0;
1109
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001110 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001111 message = _("wrong address");
Simon Kelley3be34542004-09-11 19:12:13 +01001112 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001113 }
1114 else
1115 {
1116 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +00001117 /* Check existing lease for this address.
1118 We allow it to be missing if dhcp-authoritative mode
1119 as long as we can allocate the lease now - checked below.
1120 This makes for a smooth recovery from a lost lease DB */
1121 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
Simon Kelley28866e92011-02-14 20:19:14 +00001122 (!lease && !option_bool(OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001123 {
Simon Kelley28866e92011-02-14 20:19:14 +00001124 /* A client rebinding will broadcast the request, so we may see it even
1125 if the lease is held by another server. Just ignore it in that case.
1126 If the request is unicast to us, then somethings wrong, NAK */
1127 if (!unicast_dest)
1128 return 0;
Simon Kelleyb8187c82005-11-26 21:46:27 +00001129 message = _("lease not found");
1130 /* ensure we broadcast NAK */
1131 unicast_dest = 0;
1132 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001133
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001134 /* desynchronise renewals */
1135 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +01001136 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +00001137 }
1138
Simon Kelley7622fc02009-06-04 20:32:05 +01001139 log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
Simon Kelleye17fb622006-01-14 20:33:46 +00001140
Simon Kelleydfa666f2004-08-02 18:27:27 +01001141 if (!message)
1142 {
1143 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001144 struct dhcp_context *tmp = NULL;
1145
1146 if (have_config(config, CONFIG_ADDR))
1147 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +01001148 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001149 break;
Simon Kelleyaedef832006-01-22 14:02:31 +00001150
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001151 if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001152 {
Simon Kelleye17fb622006-01-14 20:33:46 +00001153 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +00001154 message = _("wrong network");
1155 /* ensure we broadcast NAK */
1156 unicast_dest = 0;
1157 }
1158
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001159 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001160 else if (!address_available(context, mess->yiaddr, tagif_netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +01001161 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001162 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +00001163
Simon Kelleydfa666f2004-08-02 18:27:27 +01001164 /* Check if a new static address has been configured. Be very sure that
1165 when the client does DISCOVER, it will get the static address, otherwise
1166 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +00001167 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001168 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +01001169 (!have_config(config, CONFIG_DECLINED) ||
1170 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001171 config->addr.s_addr != mess->yiaddr.s_addr &&
1172 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001173 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001174
1175 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +01001176 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +00001177 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +01001178
Simon Kelley9009d742008-11-14 20:04:27 +00001179 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
1180 {
1181 /* If a host is configured with more than one MAC address, it's OK to 'nix
1182 a lease from one of it's MACs to give the address to another. */
1183 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
1184 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001185 my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
Simon Kelley9009d742008-11-14 20:04:27 +00001186 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
1187 inet_ntoa(ltmp->addr));
1188 lease = ltmp;
1189 }
Simon Kelley16972692006-10-16 20:04:18 +01001190 else
Simon Kelley9009d742008-11-14 20:04:27 +00001191 message = _("address in use");
1192 }
1193
1194 if (!message)
1195 {
1196 if (emac_len == 0)
1197 message = _("no unique-id");
1198
1199 else if (!lease)
1200 {
Simon Kelley52b92f42012-01-22 16:05:15 +00001201 if ((lease = lease4_allocate(mess->yiaddr)))
Simon Kelley9009d742008-11-14 20:04:27 +00001202 do_classes = 1;
1203 else
1204 message = _("no leases left");
1205 }
Simon Kelley16972692006-10-16 20:04:18 +01001206 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001207 }
Simon Kelley16972692006-10-16 20:04:18 +01001208
Simon Kelley44a2a312004-03-10 20:04:35 +00001209 if (message)
1210 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001211 log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelley44a2a312004-03-10 20:04:35 +00001212
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001213 mess->yiaddr.s_addr = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001214 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001215 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001216 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001217 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001218 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
1219 a distant subnet which unicast a REQ to us won't work. */
1220 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
1221 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
1222 {
1223 mess->flags |= htons(0x8000); /* broadcast */
1224 mess->ciaddr.s_addr = 0;
1225 }
Simon Kelley44a2a312004-03-10 20:04:35 +00001226 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001227 else
Simon Kelley44a2a312004-03-10 20:04:35 +00001228 {
Simon Kelley316e2732010-01-22 20:16:09 +00001229 if (context->netid.net)
1230 {
1231 context->netid.next = netid;
Simon Kelley7de060b2011-08-26 17:24:52 +01001232 tagif_netid = run_tag_if( &context->netid);
Simon Kelley316e2732010-01-22 20:16:09 +00001233 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001234
Simon Kelley4cb1b322012-02-06 14:30:41 +00001235 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001236
Simon Kelleydcffad22012-04-24 15:25:18 +01001237 if (do_classes)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001238 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001239 /* pick up INIT-REBOOT events. */
Simon Kelley4cb1b322012-02-06 14:30:41 +00001240 lease->flags |= LEASE_CHANGED;
Simon Kelley39bec5f2012-01-06 22:36:58 +00001241
Simon Kelleydcffad22012-04-24 15:25:18 +01001242#ifdef HAVE_SCRIPT
1243 if (daemon->lease_change_command)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001244 {
Simon Kelleydcffad22012-04-24 15:25:18 +01001245 struct dhcp_netid *n;
1246
1247 if (mess->giaddr.s_addr)
1248 lease->giaddr = mess->giaddr;
1249
1250 free(lease->extradata);
1251 lease->extradata = NULL;
1252 lease->extradata_size = lease->extradata_len = 0;
1253
1254 add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
1255 add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
1256 add_extradata_opt(lease, oui);
1257 add_extradata_opt(lease, serial);
1258 add_extradata_opt(lease, class);
1259
1260 /* space-concat tag set */
1261 if (!tagif_netid)
1262 add_extradata_opt(lease, NULL);
1263 else
1264 for (n = tagif_netid; n; n = n->next)
1265 {
1266 struct dhcp_netid *n1;
1267 /* kill dupes */
1268 for (n1 = n->next; n1; n1 = n1->next)
1269 if (strcmp(n->net, n1->net) == 0)
1270 break;
1271 if (!n1)
1272 lease_add_extradata(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
1273 }
1274
1275 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1276 {
1277 int len = option_len(opt);
1278 unsigned char *ucp = option_ptr(opt, 0);
1279 /* If the user-class option started as counted strings, the first byte will be zero. */
1280 if (len != 0 && ucp[0] == 0)
1281 ucp++, len--;
1282 lease_add_extradata(lease, ucp, len, 0);
1283 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001284 }
Simon Kelley316e2732010-01-22 20:16:09 +00001285#endif
Simon Kelleydcffad22012-04-24 15:25:18 +01001286 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001287
1288 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
1289 {
1290 domain = get_domain(mess->yiaddr);
Simon Kelleyb8187c82005-11-26 21:46:27 +00001291 hostname = client_hostname;
1292 hostname_auth = 1;
1293 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001294
Simon Kelley824af852008-02-12 20:43:05 +00001295 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleya9ab7322012-04-28 11:29:37 +01001296 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len, now, do_classes);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001297
Simon Kelley832af0b2007-01-21 20:01:28 +00001298 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1299 if (!hostname_auth)
1300 {
1301 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001302 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
Simon Kelley832af0b2007-01-21 20:01:28 +00001303 break;
1304 if (id_list)
1305 hostname = NULL;
1306 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001307
1308 /* Last ditch, if configured, generate hostname from mac address */
1309 if (!hostname && emac_len != 0)
1310 {
1311 for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
1312 if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
1313 break;
1314 if (id_list)
1315 {
1316 int i;
1317
1318 hostname = daemon->dhcp_buff;
1319 /* buffer is 256 bytes, 3 bytes per octet */
1320 for (i = 0; (i < emac_len) && (i < 80); i++)
1321 hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
1322 hostname = daemon->dhcp_buff;
1323 }
1324 }
1325
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001326 if (hostname)
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001327 lease_set_hostname(lease, hostname, hostname_auth, get_domain(lease->addr), domain);
Simon Kelley832af0b2007-01-21 20:01:28 +00001328
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001329 lease_set_expires(lease, time, now);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001330 lease_set_interface(lease, int_index, now);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001331
1332 if (override.s_addr != 0)
1333 lease->override = override;
1334 else
1335 override = lease->override;
1336
Simon Kelley7622fc02009-06-04 20:32:05 +01001337 log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley832af0b2007-01-21 20:01:28 +00001338
Simon Kelley7622fc02009-06-04 20:32:05 +01001339 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001340 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001341 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001342 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001343 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001344 {
Simon Kelley59353a62004-11-21 19:34:28 +00001345 while (fuzz > (time/16))
1346 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001347 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1348 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001349 }
Simon Kelley9009d742008-11-14 20:04:27 +00001350 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001351 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley44a2a312004-03-10 20:04:35 +00001352 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001353
Simon Kelley7de060b2011-08-26 17:24:52 +01001354 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001355
1356 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001357 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001358 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001359
Simon Kelley7622fc02009-06-04 20:32:05 +01001360 log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001361
Simon Kelley73a08a22009-02-05 20:28:08 +00001362 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001363 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001364
1365 /* For DHCPINFORM only, cope without a valid context */
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001366 context = narrow_context(context, mess->ciaddr, tagif_netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001367
Simon Kelley5aabfc72007-08-29 11:24:47 +01001368 /* Find a least based on IP address if we didn't
1369 get one from MAC address/client-d */
1370 if (!lease &&
1371 (lease = lease_find_by_addr(mess->ciaddr)) &&
1372 lease->hostname)
1373 hostname = lease->hostname;
1374
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001375 if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
1376 domain = get_domain(mess->ciaddr);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001377
Simon Kelley73a08a22009-02-05 20:28:08 +00001378 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001379 {
1380 context->netid.next = netid;
Simon Kelley4cb1b322012-02-06 14:30:41 +00001381 tagif_netid = run_tag_if(&context->netid);
Simon Kelley59353a62004-11-21 19:34:28 +00001382 }
Simon Kelley7de060b2011-08-26 17:24:52 +01001383
Simon Kelley4cb1b322012-02-06 14:30:41 +00001384 log_tags(tagif_netid, ntohl(mess->xid));
Simon Kelley7de060b2011-08-26 17:24:52 +01001385
1386 log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001387
Simon Kelley3927da42008-07-20 15:10:39 +01001388 if (lease)
1389 {
1390 if (override.s_addr != 0)
1391 lease->override = override;
1392 else
1393 override = lease->override;
1394 }
1395
Simon Kelley7622fc02009-06-04 20:32:05 +01001396 clear_packet(mess, end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001397 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001398 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1399
Simon Kelley5aabfc72007-08-29 11:24:47 +01001400 if (lease)
1401 {
1402 if (lease->expires == 0)
1403 time = 0xffffffff;
1404 else
1405 time = (unsigned int)difftime(lease->expires, now);
1406 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley353ae4d2012-03-19 20:07:51 +00001407 lease_set_interface(lease, int_index, now);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001408 }
Simon Kelley824af852008-02-12 20:43:05 +00001409
Simon Kelley9009d742008-11-14 20:04:27 +00001410 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
Simon Kelley70c5e3e2012-02-06 22:05:15 +00001411 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001412
Simon Kelley5aabfc72007-08-29 11:24:47 +01001413 *is_inform = 1; /* handle reply differently */
Simon Kelley7de060b2011-08-26 17:24:52 +01001414 return dhcp_packet_size(mess, agent_id, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001415 }
1416
1417 return 0;
1418}
1419
Simon Kelley6b010842007-02-12 20:32:07 +00001420/* find a good value to use as MAC address for logging and address-allocation hashing.
1421 This is normally just the chaddr field from the DHCP packet,
1422 but eg Firewire will have hlen == 0 and use the client-id instead.
1423 This could be anything, but will normally be EUI64 for Firewire.
1424 We assume that if the first byte of the client-id equals the htype byte
1425 then the client-id is using the usual encoding and use the rest of the
1426 client-id: if not we can use the whole client-id. This should give
1427 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001428unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001429 int clid_len, unsigned char *clid, int *len_out)
1430{
1431 if (hwlen == 0 && clid && clid_len > 3)
1432 {
1433 if (clid[0] == hwtype)
1434 {
1435 *len_out = clid_len - 1 ;
1436 return clid + 1;
1437 }
1438
1439#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1440 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1441 {
1442 *len_out = clid_len - 1 ;
1443 return clid + 1;
1444 }
1445#endif
1446
1447 *len_out = clid_len;
1448 return clid;
1449 }
1450
1451 *len_out = hwlen;
1452 return hwaddr;
1453}
1454
Simon Kelley824af852008-02-12 20:43:05 +00001455static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001456{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001457 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001458
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001459 if (opt)
1460 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001461 unsigned int req_time = option_uint(opt, 0, 4);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001462 if (req_time < 120 )
1463 req_time = 120; /* sanity */
1464 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1465 time = req_time;
1466 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001467
1468 return time;
1469}
1470
Simon Kelley73a08a22009-02-05 20:28:08 +00001471static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001472{
Simon Kelley73a08a22009-02-05 20:28:08 +00001473 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001474 return override;
Simon Kelley7de060b2011-08-26 17:24:52 +01001475 else if (context && context->local.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001476 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001477 else
1478 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001479}
1480
Simon Kelleyf2621c72007-04-29 19:47:21 +01001481static int sanitise(unsigned char *opt, char *buf)
1482{
1483 char *p;
1484 int i;
1485
1486 *buf = 0;
1487
1488 if (!opt)
1489 return 0;
1490
Simon Kelley1a6bca82008-07-11 11:11:42 +01001491 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001492
1493 for (i = option_len(opt); i > 0; i--)
1494 {
1495 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001496 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001497 *buf++ = c;
1498 }
1499 *buf = 0; /* add terminator */
1500
1501 return 1;
1502}
1503
Simon Kelley316e2732010-01-22 20:16:09 +00001504#ifdef HAVE_SCRIPT
Simon Kelley316e2732010-01-22 20:16:09 +00001505static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
1506{
1507 if (!opt)
Simon Kelleyceae00d2012-02-09 21:28:14 +00001508 lease_add_extradata(lease, NULL, 0, 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001509 else
Simon Kelleyceae00d2012-02-09 21:28:14 +00001510 lease_add_extradata(lease, option_ptr(opt, 0), option_len(opt), 0);
Simon Kelley316e2732010-01-22 20:16:09 +00001511}
1512#endif
1513
Simon Kelley5aabfc72007-08-29 11:24:47 +01001514static void log_packet(char *type, void *addr, unsigned char *ext_mac,
Simon Kelley7622fc02009-06-04 20:32:05 +01001515 int mac_len, char *interface, char *string, u32 xid)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001516{
Simon Kelley16972692006-10-16 20:04:18 +01001517 struct in_addr a;
Simon Kelley7622fc02009-06-04 20:32:05 +01001518
Simon Kelley16972692006-10-16 20:04:18 +01001519 /* addr may be misaligned */
1520 if (addr)
1521 memcpy(&a, addr, sizeof(a));
1522
Simon Kelley7622fc02009-06-04 20:32:05 +01001523 print_mac(daemon->namebuff, ext_mac, mac_len);
1524
Simon Kelley28866e92011-02-14 20:19:14 +00001525 if(option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001526 my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
1527 ntohl(xid),
1528 type,
1529 interface,
1530 addr ? inet_ntoa(a) : "",
1531 addr ? " " : "",
1532 daemon->namebuff,
1533 string ? string : "");
1534 else
1535 my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
1536 type,
1537 interface,
1538 addr ? inet_ntoa(a) : "",
1539 addr ? " " : "",
1540 daemon->namebuff,
1541 string ? string : "");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001542}
1543
Simon Kelley7622fc02009-06-04 20:32:05 +01001544static void log_options(unsigned char *start, u32 xid)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001545{
1546 while (*start != OPTION_END)
1547 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00001548 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 +01001549
Simon Kelley4cb1b322012-02-06 14:30:41 +00001550 my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d %s %s",
1551 ntohl(xid), option_len(start), start[0], optname, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001552 start += start[1] + 2;
1553 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001554}
1555
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001556static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001557{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001558 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001559 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001560 if (p > end)
1561 return NULL;
1562 else if (*p == OPTION_END)
1563 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001564 else if (*p == OPTION_PAD)
1565 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001566 else
1567 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001568 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001569 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001570 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001571 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001572 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001573 return NULL; /* malformed packet */
1574 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001575 return p;
1576 p += opt_len + 2;
1577 }
1578 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001579}
1580
Simon Kelleycdeda282006-03-16 20:16:06 +00001581static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001582{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001583 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001584
Simon Kelley3be34542004-09-11 19:12:13 +01001585 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001586 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1587 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001588
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001589 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001590 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001591 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001592
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001593 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001594 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001595 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1596 return ret;
1597
1598 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001599 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001600 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1601 return ret;
1602
1603 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001604}
1605
Simon Kelley4cb1b322012-02-06 14:30:41 +00001606static struct in_addr option_addr(unsigned char *opt)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001607{
Simon Kelley4cb1b322012-02-06 14:30:41 +00001608 /* this worries about unaligned data in the option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001609 /* struct in_addr is network byte order */
1610 struct in_addr ret;
1611
Simon Kelley4cb1b322012-02-06 14:30:41 +00001612 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001613
1614 return ret;
1615}
1616
Simon Kelley7622fc02009-06-04 20:32:05 +01001617static unsigned int option_uint(unsigned char *opt, int offset, int size)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001618{
1619 /* this worries about unaligned data and byte order */
1620 unsigned int ret = 0;
1621 int i;
Simon Kelley7622fc02009-06-04 20:32:05 +01001622 unsigned char *p = option_ptr(opt, offset);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001623
1624 for (i = 0; i < size; i++)
1625 ret = (ret << 8) | *p++;
1626
1627 return ret;
1628}
1629
1630static unsigned char *dhcp_skip_opts(unsigned char *start)
1631{
1632 while (*start != 0)
1633 start += start[1] + 2;
1634 return start;
1635}
1636
1637/* only for use when building packet: doesn't check for bad data. */
1638static unsigned char *find_overload(struct dhcp_packet *mess)
1639{
1640 unsigned char *p = &mess->options[0] + sizeof(u32);
1641
1642 while (*p != 0)
1643 {
1644 if (*p == OPTION_OVERLOAD)
1645 return p;
1646 p += p[1] + 2;
1647 }
1648 return NULL;
1649}
1650
Simon Kelley7de060b2011-08-26 17:24:52 +01001651static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
1652{
1653 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1654 unsigned char *overload;
1655 size_t ret;
1656
1657 /* move agent_id back down to the end of the packet */
1658 if (agent_id)
1659 {
1660 memmove(p, agent_id, real_end - agent_id);
1661 p += real_end - agent_id;
1662 memset(p, 0, real_end - p); /* in case of overlap */
1663 }
1664
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001665 /* add END options to the regions. */
Simon Kelley7622fc02009-06-04 20:32:05 +01001666 overload = find_overload(mess);
1667
1668 if (overload && (option_uint(overload, 0, 1) & 1))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001669 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001670 *dhcp_skip_opts(mess->file) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001671 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001672 log_options(mess->file, mess->xid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001673 }
Simon Kelley28866e92011-02-14 20:19:14 +00001674 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001675 my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
1676
1677 if (overload && (option_uint(overload, 0, 1) & 2))
1678 {
1679 *dhcp_skip_opts(mess->sname) = OPTION_END;
Simon Kelley28866e92011-02-14 20:19:14 +00001680 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001681 log_options(mess->sname, mess->xid);
1682 }
Simon Kelley28866e92011-02-14 20:19:14 +00001683 else if (option_bool(OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01001684 my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
1685
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001686
1687 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001688
Simon Kelley28866e92011-02-14 20:19:14 +00001689 if (option_bool(OPT_LOG_OPTS))
Simon Kelley7622fc02009-06-04 20:32:05 +01001690 {
1691 if (mess->siaddr.s_addr != 0)
1692 my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
1693
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001694 if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
1695 my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
1696
Simon Kelley7622fc02009-06-04 20:32:05 +01001697 log_options(&mess->options[0] + sizeof(u32), mess->xid);
1698 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001699
1700 ret = (size_t)(p - (unsigned char *)mess);
1701
1702 if (ret < MIN_PACKETSZ)
1703 ret = MIN_PACKETSZ;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001704
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001705 return ret;
1706}
1707
1708static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1709{
1710 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1711
1712 if (p + len + 3 >= end)
1713 /* not enough space in options area, try and use overload, if poss */
1714 {
1715 unsigned char *overload;
1716
1717 if (!(overload = find_overload(mess)) &&
1718 (mess->file[0] == 0 || mess->sname[0] == 0))
1719 {
1720 /* attempt to overload fname and sname areas, we've reserved space for the
1721 overflow option previuously. */
1722 overload = p;
1723 *(p++) = OPTION_OVERLOAD;
1724 *(p++) = 1;
1725 }
1726
1727 p = NULL;
1728
1729 /* using filename field ? */
1730 if (overload)
1731 {
1732 if (mess->file[0] == 0)
1733 overload[2] |= 1;
1734
1735 if (overload[2] & 1)
1736 {
1737 p = dhcp_skip_opts(mess->file);
1738 if (p + len + 3 >= mess->file + sizeof(mess->file))
1739 p = NULL;
1740 }
1741
1742 if (!p)
1743 {
1744 /* try to bring sname into play (it may be already) */
1745 if (mess->sname[0] == 0)
1746 overload[2] |= 2;
1747
1748 if (overload[2] & 2)
1749 {
1750 p = dhcp_skip_opts(mess->sname);
1751 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1752 p = NULL;
1753 }
1754 }
1755 }
1756
1757 if (!p)
Simon Kelley7622fc02009-06-04 20:32:05 +01001758 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 +00001759 }
1760
1761 if (p)
1762 {
1763 *(p++) = opt;
1764 *(p++) = len;
1765 }
1766
1767 return p;
1768}
1769
1770static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1771{
1772 int i;
1773 unsigned char *p = free_space(mess, end, opt, len);
1774
1775 if (p)
1776 for (i = 0; i < len; i++)
1777 *(p++) = val >> (8 * (len - (i + 1)));
1778}
1779
1780static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1781 char *string, int null_term)
1782{
1783 unsigned char *p;
1784 size_t len = strlen(string);
1785
1786 if (null_term && len != 255)
1787 len++;
1788
1789 if ((p = free_space(mess, end, opt, len)))
1790 memcpy(p, string, len);
1791}
1792
1793/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001794static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001795{
1796 int len = opt->len;
1797
1798 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1799 len++;
1800
1801 if (p && len != 0)
1802 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001803 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001804 {
1805 int j;
1806 struct in_addr *a = (struct in_addr *)opt->val;
1807 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1808 {
1809 /* zero means "self" (but not in vendorclass options.) */
1810 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001811 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001812 else
1813 memcpy(p, a, INADDRSZ);
1814 p += INADDRSZ;
1815 }
1816 }
1817 else
1818 memcpy(p, opt->val, len);
1819 }
1820 return len;
1821}
Simon Kelley7622fc02009-06-04 20:32:05 +01001822
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001823static int in_list(unsigned char *list, int opt)
1824{
1825 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001826
1827 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001828 if (!list)
1829 return 1;
1830
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001831 for (i = 0; list[i] != OPTION_END; i++)
1832 if (opt == list[i])
1833 return 1;
1834
1835 return 0;
1836}
1837
Simon Kelley7de060b2011-08-26 17:24:52 +01001838static struct dhcp_opt *option_find2(int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001839{
Simon Kelley7de060b2011-08-26 17:24:52 +01001840 struct dhcp_opt *opts;
1841
1842 for (opts = daemon->dhcp_opts; opts; opts = opts->next)
1843 if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
1844 return opts;
1845
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001846 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001847}
1848
Simon Kelley6b010842007-02-12 20:32:07 +00001849/* mark vendor-encapsulated options which match the client-supplied or
1850 config-supplied vendor class */
1851static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1852{
1853 for (; dopt; dopt = dopt->next)
1854 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001855 dopt->flags &= ~DHOPT_VENDOR_MATCH;
Simon Kelley73a08a22009-02-05 20:28:08 +00001856 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001857 {
1858 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001859 if (dopt->u.vendor_class)
1860 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001861 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001862 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001863 {
Simon Kelley7622fc02009-06-04 20:32:05 +01001864 dopt->flags |= DHOPT_VENDOR_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001865 break;
1866 }
1867 }
1868 }
1869}
1870
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001871static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
1872 struct dhcp_packet *mess, unsigned char *end, int null_term)
Simon Kelley73a08a22009-02-05 20:28:08 +00001873{
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001874 int len, enc_len, ret = 0;
Simon Kelley7622fc02009-06-04 20:32:05 +01001875 struct dhcp_opt *start;
Simon Kelley73a08a22009-02-05 20:28:08 +00001876 unsigned char *p;
1877
1878 /* find size in advance */
Simon Kelley7622fc02009-06-04 20:32:05 +01001879 for (enc_len = 0, start = opt; opt; opt = opt->next)
1880 if (opt->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001881 {
1882 int new = do_opt(opt, NULL, NULL, null_term) + 2;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001883 ret = 1;
Simon Kelley73a08a22009-02-05 20:28:08 +00001884 if (enc_len + new <= 255)
1885 enc_len += new;
1886 else
1887 {
1888 p = free_space(mess, end, encap, enc_len);
1889 for (; start && start != opt; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001890 if (p && (start->flags & flag))
Simon Kelley73a08a22009-02-05 20:28:08 +00001891 {
1892 len = do_opt(start, p + 2, NULL, null_term);
1893 *(p++) = start->opt;
1894 *(p++) = len;
1895 p += len;
1896 }
1897 enc_len = new;
1898 start = opt;
1899 }
1900 }
1901
1902 if (enc_len != 0 &&
1903 (p = free_space(mess, end, encap, enc_len + 1)))
1904 {
1905 for (; start; start = start->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01001906 if (start->flags & flag)
Simon Kelley73a08a22009-02-05 20:28:08 +00001907 {
1908 len = do_opt(start, p + 2, NULL, null_term);
1909 *(p++) = start->opt;
1910 *(p++) = len;
1911 p += len;
1912 }
1913 *p = OPTION_END;
1914 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001915
1916 return ret;
Simon Kelley73a08a22009-02-05 20:28:08 +00001917}
1918
Simon Kelley7622fc02009-06-04 20:32:05 +01001919static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
Simon Kelley91dccd02005-03-31 17:48:32 +01001920{
Simon Kelley7622fc02009-06-04 20:32:05 +01001921 unsigned char *p;
Simon Kelley9e038942008-05-30 20:06:34 +01001922
Simon Kelley7622fc02009-06-04 20:32:05 +01001923 option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
1924 if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
1925 memcpy(p, uuid, 17);
1926}
1927
1928static int prune_vendor_opts(struct dhcp_netid *netid)
1929{
1930 int force = 0;
1931 struct dhcp_opt *opt;
1932
1933 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
1934 for (opt = daemon->dhcp_opts; opt; opt = opt->next)
1935 if (opt->flags & DHOPT_VENDOR_MATCH)
1936 {
1937 if (!match_netid(opt->netid, netid, 1))
1938 opt->flags &= ~DHOPT_VENDOR_MATCH;
1939 else if (opt->flags & DHOPT_FORCE)
1940 force = 1;
1941 }
1942 return force;
1943}
1944
Simon Kelley751d6f42012-02-10 15:24:51 +00001945static 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 +01001946{
1947#define NUM_OPTS 4
1948
1949 unsigned char *p, *q;
1950 struct pxe_service *service;
1951 static struct dhcp_opt *o, *ret;
1952 int i, j = NUM_OPTS - 1;
Simon Kelley316e2732010-01-22 20:16:09 +00001953 struct in_addr boot_server;
Simon Kelley7622fc02009-06-04 20:32:05 +01001954
1955 /* We pass back references to these, hence they are declared static */
1956 static unsigned char discovery_control;
1957 static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' };
1958 static struct dhcp_opt *fake_opts = NULL;
1959
Simon Kelley316e2732010-01-22 20:16:09 +00001960 /* Disable multicast, since we don't support it, and broadcast
1961 unless we need it */
1962 discovery_control = 3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001963
1964 ret = daemon->dhcp_opts;
1965
1966 if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
1967 return ret;
1968
1969 for (i = 0; i < NUM_OPTS; i++)
1970 {
1971 fake_opts[i].flags = DHOPT_VENDOR_MATCH;
1972 fake_opts[i].netid = NULL;
1973 fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
1974 }
1975
1976 /* create the data for the PXE_MENU and PXE_SERVERS options. */
1977 p = (unsigned char *)daemon->dhcp_buff;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01001978 q = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01001979
1980 for (i = 0, service = daemon->pxe_services; service; service = service->next)
1981 if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
1982 {
1983 size_t len = strlen(service->menu);
1984 /* opt 43 max size is 255. encapsulated option has type and length
1985 bytes, so its max size is 253. */
1986 if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
1987 {
1988 *(p++) = service->type >> 8;
1989 *(p++) = service->type;
1990 *(p++) = len;
1991 memcpy(p, service->menu, len);
1992 p += len;
1993 i++;
1994 }
1995 else
1996 {
1997 toobig:
1998 my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
1999 return daemon->dhcp_opts;
2000 }
2001
Simon Kelley751d6f42012-02-10 15:24:51 +00002002 boot_server = service->basename ? local :
2003 (service->sname ? a_record_from_hosts(service->sname, now) : service->server);
2004
Simon Kelley316e2732010-01-22 20:16:09 +00002005 if (boot_server.s_addr != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002006 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002007 if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
Simon Kelley316e2732010-01-22 20:16:09 +00002008 goto toobig;
2009
2010 /* Boot service with known address - give it */
2011 *(q++) = service->type >> 8;
2012 *(q++) = service->type;
2013 *(q++) = 1;
2014 /* dest misaligned */
2015 memcpy(q, &boot_server.s_addr, INADDRSZ);
2016 q += INADDRSZ;
2017 }
2018 else if (service->type != 0)
2019 /* We don't know the server for a service type, so we'll
2020 allow the client to broadcast for it */
2021 discovery_control = 2;
Simon Kelley7622fc02009-06-04 20:32:05 +01002022 }
2023
2024 /* if no prompt, wait forever if there's a choice */
2025 fake_prompt[0] = (i > 1) ? 255 : 0;
2026
2027 if (i == 0)
2028 discovery_control = 8; /* no menu - just use use mess->filename */
2029 else
2030 {
2031 ret = &fake_opts[j--];
2032 ret->len = p - (unsigned char *)daemon->dhcp_buff;
2033 ret->val = (unsigned char *)daemon->dhcp_buff;
2034 ret->opt = SUBOPT_PXE_MENU;
2035
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002036 if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
Simon Kelley7622fc02009-06-04 20:32:05 +01002037 {
2038 ret = &fake_opts[j--];
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002039 ret->len = q - (unsigned char *)daemon->dhcp_buff3;
2040 ret->val = (unsigned char *)daemon->dhcp_buff3;
Simon Kelley7622fc02009-06-04 20:32:05 +01002041 ret->opt = SUBOPT_PXE_SERVERS;
2042 }
2043 }
2044
2045 for (o = daemon->dhcp_opts; o; o = o->next)
2046 if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
2047 break;
2048
2049 if (!o)
2050 {
2051 ret = &fake_opts[j--];
2052 ret->len = sizeof(fake_prompt);
2053 ret->val = fake_prompt;
2054 ret->opt = SUBOPT_PXE_MENU_PROMPT;
2055 }
2056
Simon Kelley316e2732010-01-22 20:16:09 +00002057 ret = &fake_opts[j--];
2058 ret->len = 1;
2059 ret->opt = SUBOPT_PXE_DISCOVERY;
2060 ret->val= &discovery_control;
2061
Simon Kelley7622fc02009-06-04 20:32:05 +01002062 return ret;
2063}
2064
2065static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
2066{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002067 memset(mess->sname, 0, sizeof(mess->sname));
2068 memset(mess->file, 0, sizeof(mess->file));
2069 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
2070 mess->siaddr.s_addr = 0;
2071}
Simon Kelleycdeda282006-03-16 20:16:06 +00002072
Simon Kelley7622fc02009-06-04 20:32:05 +01002073struct dhcp_boot *find_boot(struct dhcp_netid *netid)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002074{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002075 struct dhcp_boot *boot;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002076
2077 /* decide which dhcp-boot option we're using */
2078 for (boot = daemon->boot_config; boot; boot = boot->next)
2079 if (match_netid(boot->netid, netid, 0))
2080 break;
2081 if (!boot)
2082 /* No match, look for one without a netid */
2083 for (boot = daemon->boot_config; boot; boot = boot->next)
2084 if (match_netid(boot->netid, netid, 1))
2085 break;
Simon Kelley7622fc02009-06-04 20:32:05 +01002086
2087 return boot;
2088}
2089
2090static void do_options(struct dhcp_context *context,
2091 struct dhcp_packet *mess,
2092 unsigned char *end,
2093 unsigned char *req_options,
2094 char *hostname,
Simon Kelley70c5e3e2012-02-06 22:05:15 +00002095 char *domain,
Simon Kelley7622fc02009-06-04 20:32:05 +01002096 struct dhcp_netid *netid,
2097 struct in_addr subnet_addr,
2098 unsigned char fqdn_flags,
2099 int null_term, int pxe_arch,
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002100 unsigned char *uuid,
Simon Kelley7de060b2011-08-26 17:24:52 +01002101 int vendor_class_len,
2102 time_t now)
Simon Kelley7622fc02009-06-04 20:32:05 +01002103{
2104 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
2105 struct dhcp_boot *boot;
2106 unsigned char *p;
2107 int i, len, force_encap = 0;
2108 unsigned char f0 = 0, s0 = 0;
2109 int done_file = 0, done_server = 0;
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002110 int done_vendor_class = 0;
Simon Kelley7de060b2011-08-26 17:24:52 +01002111 struct dhcp_netid *tagif;
2112 struct dhcp_netid_list *id_list;
Simon Kelley7622fc02009-06-04 20:32:05 +01002113
Simon Kelley4cb1b322012-02-06 14:30:41 +00002114 /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
Simon Kelley7d2b5c92012-03-23 10:00:02 +00002115 if (context)
2116 context->netid.next = NULL;
Simon Kelley57f460d2012-02-16 20:00:32 +00002117 tagif = option_filter(netid, context && context->netid.net ? &context->netid : NULL, config_opts);
Simon Kelley7de060b2011-08-26 17:24:52 +01002118
Simon Kelley7622fc02009-06-04 20:32:05 +01002119 /* logging */
Simon Kelley28866e92011-02-14 20:19:14 +00002120 if (option_bool(OPT_LOG_OPTS) && req_options)
Simon Kelley7622fc02009-06-04 20:32:05 +01002121 {
2122 char *q = daemon->namebuff;
2123 for (i = 0; req_options[i] != OPTION_END; i++)
2124 {
Simon Kelley4cb1b322012-02-06 14:30:41 +00002125 char *s = option_string(AF_INET, req_options[i], NULL, 0, NULL, 0);
Simon Kelley7622fc02009-06-04 20:32:05 +01002126 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
2127 "%d%s%s%s",
2128 req_options[i],
Simon Kelley4cb1b322012-02-06 14:30:41 +00002129 strlen(s) != 0 ? ":" : "",
2130 s,
Simon Kelley7622fc02009-06-04 20:32:05 +01002131 req_options[i+1] == OPTION_END ? "" : ", ");
2132 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
2133 {
2134 q = daemon->namebuff;
2135 my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
2136 }
2137 }
2138 }
2139
Simon Kelley7de060b2011-08-26 17:24:52 +01002140 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
2141 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
2142 break;
2143 if (id_list)
2144 mess->flags |= htons(0x8000); /* force broadcast */
2145
Simon Kelley73a08a22009-02-05 20:28:08 +00002146 if (context)
2147 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002148
2149 /* See if we can send the boot stuff as options.
2150 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00002151 and very old DHCP clients won't have this, we also
2152 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002153 Some PXE ROMs have bugs (surprise!) and need zero-terminated
Simon Kelley7622fc02009-06-04 20:32:05 +01002154 names, so we always send those. */
Simon Kelley7de060b2011-08-26 17:24:52 +01002155 if ((boot = find_boot(tagif)))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002156 {
2157 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00002158 {
Simon Kelley28866e92011-02-14 20:19:14 +00002159 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002160 req_options &&
2161 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002162 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
2163 else
Simon Kelley824af852008-02-12 20:43:05 +00002164 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002165 }
2166
2167 if (boot->file)
2168 {
Simon Kelley28866e92011-02-14 20:19:14 +00002169 if (!option_bool(OPT_NO_OVERRIDE) &&
Simon Kelley824af852008-02-12 20:43:05 +00002170 req_options &&
2171 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002172 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
2173 else
Simon Kelley824af852008-02-12 20:43:05 +00002174 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002175 }
2176
Simon Kelley7de060b2011-08-26 17:24:52 +01002177 if (boot->next_server.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002178 mess->siaddr = boot->next_server;
Simon Kelley7de060b2011-08-26 17:24:52 +01002179 else if (boot->tftp_sname)
2180 mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002181 }
Simon Kelley824af852008-02-12 20:43:05 +00002182 else
2183 /* Use the values of the relevant options if no dhcp-boot given and
Simon Kelley1f15b812009-10-13 17:49:32 +01002184 they're not explicitly asked for as options. OPTION_END is used
2185 as an internal way to specify siaddr without using dhcp-boot, for use in
2186 dhcp-optsfile. */
Simon Kelley824af852008-02-12 20:43:05 +00002187 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002188 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002189 (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002190 {
2191 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
2192 done_file = 1;
2193 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002194
Simon Kelley824af852008-02-12 20:43:05 +00002195 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002196 (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
Simon Kelley824af852008-02-12 20:43:05 +00002197 {
2198 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
2199 done_server = 1;
2200 }
Simon Kelley1f15b812009-10-13 17:49:32 +01002201
Simon Kelley7de060b2011-08-26 17:24:52 +01002202 if ((opt = option_find2(OPTION_END)))
Simon Kelley1f15b812009-10-13 17:49:32 +01002203 mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
Simon Kelley824af852008-02-12 20:43:05 +00002204 }
Simon Kelley7622fc02009-06-04 20:32:05 +01002205
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002206 /* We don't want to do option-overload for BOOTP, so make the file and sname
2207 fields look like they are in use, even when they aren't. This gets restored
2208 at the end of this function. */
2209
Simon Kelley28866e92011-02-14 20:19:14 +00002210 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002211 {
2212 f0 = mess->file[0];
2213 mess->file[0] = 1;
2214 s0 = mess->sname[0];
2215 mess->sname[0] = 1;
2216 }
2217
2218 /* At this point, if mess->sname or mess->file are zeroed, they are available
2219 for option overload, reserve space for the overload option. */
2220 if (mess->file[0] == 0 || mess->sname[0] == 0)
2221 end -= 3;
2222
Simon Kelley3be34542004-09-11 19:12:13 +01002223 /* rfc3011 says this doesn't need to be in the requested options list. */
2224 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002225 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00002226
2227 /* replies to DHCPINFORM may not have a valid context */
2228 if (context)
2229 {
Simon Kelley7de060b2011-08-26 17:24:52 +01002230 if (!option_find2(OPTION_NETMASK))
Simon Kelley73a08a22009-02-05 20:28:08 +00002231 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
2232
2233 /* May not have a "guessed" broadcast address if we got no packets via a relay
2234 from this net yet (ie just unicast renewals after a restart */
2235 if (context->broadcast.s_addr &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002236 !option_find2(OPTION_BROADCAST))
Simon Kelley73a08a22009-02-05 20:28:08 +00002237 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
2238
2239 /* Same comments as broadcast apply, and also may not be able to get a sensible
2240 default when using subnet select. User must configure by steam in that case. */
2241 if (context->router.s_addr &&
2242 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002243 !option_find2(OPTION_ROUTER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002244 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
2245
2246 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002247 !option_find2(OPTION_DNSSERVER))
Simon Kelley73a08a22009-02-05 20:28:08 +00002248 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
2249 }
Simon Kelley3be34542004-09-11 19:12:13 +01002250
Simon Kelley9009d742008-11-14 20:04:27 +00002251 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002252 !option_find2(OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00002253 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002254
Simon Kelley824af852008-02-12 20:43:05 +00002255 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01002256 if (hostname)
2257 {
Simon Kelley824af852008-02-12 20:43:05 +00002258 if (in_list(req_options, OPTION_HOSTNAME) &&
Simon Kelley7de060b2011-08-26 17:24:52 +01002259 !option_find2(OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002260 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01002261
2262 if (fqdn_flags != 0)
2263 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002264 len = strlen(hostname) + 3;
2265
Simon Kelley3d8df262005-08-29 12:19:27 +01002266 if (fqdn_flags & 0x04)
2267 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00002268 else if (null_term)
2269 len++;
2270
Simon Kelley9009d742008-11-14 20:04:27 +00002271 if (domain)
2272 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01002273
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002274 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01002275 {
Simon Kelley2e34ac12012-08-29 14:15:25 +01002276 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */
Simon Kelley3d8df262005-08-29 12:19:27 +01002277 *(p++) = 255;
2278 *(p++) = 255;
2279
2280 if (fqdn_flags & 0x04)
2281 {
2282 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002283 if (domain)
2284 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01002285 *p++ = 0;
2286 }
2287 else
2288 {
2289 memcpy(p, hostname, strlen(hostname));
2290 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00002291 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01002292 {
2293 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00002294 memcpy(p, domain, strlen(domain));
2295 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00002296 }
2297 if (null_term)
2298 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01002299 }
2300 }
2301 }
2302 }
2303
Simon Kelley6b010842007-02-12 20:32:07 +00002304 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002305 {
Simon Kelley824af852008-02-12 20:43:05 +00002306 int optno = opt->opt;
2307
Simon Kelley7de060b2011-08-26 17:24:52 +01002308 /* netids match and not encapsulated? */
2309 if (!(opt->flags & DHOPT_TAGOK))
2310 continue;
2311
Simon Kelley6b010842007-02-12 20:32:07 +00002312 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00002313 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00002314 continue;
2315
2316 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00002317 if (optno == OPTION_CLIENT_FQDN ||
2318 optno == OPTION_MAXMESSAGE ||
2319 optno == OPTION_OVERLOAD ||
2320 optno == OPTION_PAD ||
2321 optno == OPTION_END)
2322 continue;
2323
2324 if (optno == OPTION_SNAME && done_server)
2325 continue;
2326
2327 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00002328 continue;
2329
Simon Kelley33820b72004-04-03 21:10:00 +01002330 /* For the options we have default values on
2331 dhc-option=<optionno> means "don't include this option"
2332 not "include a zero-length option" */
2333 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00002334 (optno == OPTION_NETMASK ||
2335 optno == OPTION_BROADCAST ||
2336 optno == OPTION_ROUTER ||
2337 optno == OPTION_DNSSERVER ||
2338 optno == OPTION_DOMAINNAME ||
2339 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01002340 continue;
Simon Kelley7622fc02009-06-04 20:32:05 +01002341
2342 /* vendor-class comes from elsewhere for PXE */
2343 if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
2344 continue;
Simon Kelley824af852008-02-12 20:43:05 +00002345
Simon Kelley7622fc02009-06-04 20:32:05 +01002346 /* always force null-term for filename and servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00002347 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00002348 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01002349
Simon Kelley824af852008-02-12 20:43:05 +00002350 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00002351 {
Simon Kelley73a08a22009-02-05 20:28:08 +00002352 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00002353 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
2354
Simon Kelley6b010842007-02-12 20:32:07 +00002355 /* If we send a vendor-id, revisit which vendor-ops we consider
2356 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00002357 if (optno == OPTION_VENDOR_ID)
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002358 {
2359 match_vendor_opts(p - 2, config_opts);
2360 done_vendor_class = 1;
2361 }
Simon Kelley6b010842007-02-12 20:32:07 +00002362 }
2363 }
2364
Simon Kelley73a08a22009-02-05 20:28:08 +00002365 /* Now send options to be encapsulated in arbitrary options,
2366 eg dhcp-option=encap:172,17,.......
Simon Kelley4cb1b322012-02-06 14:30:41 +00002367 Also handle vendor-identifying vendor-encapsulated options,
2368 dhcp-option = vi-encap:13,17,.......
Simon Kelley73a08a22009-02-05 20:28:08 +00002369 The may be more that one "outer" to do, so group
2370 all the options which match each outer in turn. */
2371 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley7622fc02009-06-04 20:32:05 +01002372 opt->flags &= ~DHOPT_ENCAP_DONE;
2373
2374 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley316e2732010-01-22 20:16:09 +00002375 {
2376 int flags;
2377
2378 if ((flags = (opt->flags & (DHOPT_ENCAPSULATE | DHOPT_RFC3925))))
2379 {
2380 int found = 0;
2381 struct dhcp_opt *o;
2382
2383 if (opt->flags & DHOPT_ENCAP_DONE)
2384 continue;
2385
2386 for (len = 0, o = config_opts; o; o = o->next)
2387 {
2388 int outer = flags & DHOPT_ENCAPSULATE ? o->u.encap : OPTION_VENDOR_IDENT_OPT;
2389
2390 o->flags &= ~DHOPT_ENCAP_MATCH;
2391
2392 if (!(o->flags & flags) || opt->u.encap != o->u.encap)
2393 continue;
2394
2395 o->flags |= DHOPT_ENCAP_DONE;
Simon Kelley7de060b2011-08-26 17:24:52 +01002396 if (match_netid(o->netid, tagif, 1) &&
Simon Kelley316e2732010-01-22 20:16:09 +00002397 ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
2398 {
2399 o->flags |= DHOPT_ENCAP_MATCH;
2400 found = 1;
2401 len += do_opt(o, NULL, NULL, 0) + 2;
2402 }
2403 }
2404
2405 if (found)
2406 {
2407 if (flags & DHOPT_ENCAPSULATE)
2408 do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
2409 else if (len > 250)
2410 my_syslog(MS_DHCP | LOG_WARNING, _("cannot send RFC3925 option: too many options for enterprise number %d"), opt->u.encap);
2411 else if ((p = free_space(mess, end, OPTION_VENDOR_IDENT_OPT, len + 5)))
2412 {
2413 int swap_ent = htonl(opt->u.encap);
2414 memcpy(p, &swap_ent, 4);
2415 p += 4;
2416 *(p++) = len;
2417 for (o = config_opts; o; o = o->next)
2418 if (o->flags & DHOPT_ENCAP_MATCH)
2419 {
2420 len = do_opt(o, p + 2, NULL, 0);
2421 *(p++) = o->opt;
2422 *(p++) = len;
2423 p += len;
2424 }
2425 }
2426 }
2427 }
2428 }
Simon Kelley73a08a22009-02-05 20:28:08 +00002429
Simon Kelley7de060b2011-08-26 17:24:52 +01002430 force_encap = prune_vendor_opts(tagif);
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002431
Simon Kelley316e2732010-01-22 20:16:09 +00002432 if (context && pxe_arch != -1)
Simon Kelley7622fc02009-06-04 20:32:05 +01002433 {
2434 pxe_misc(mess, end, uuid);
Simon Kelley751d6f42012-02-10 15:24:51 +00002435 config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
Simon Kelley7622fc02009-06-04 20:32:05 +01002436 }
2437
Simon Kelley8ef5ada2010-06-03 19:42:45 +01002438 if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
2439 do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
2440 pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
2441 (p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
2442 /* If we send vendor encapsulated options, and haven't already sent option 60,
2443 echo back the value we got from the client. */
2444 memcpy(p, daemon->dhcp_buff3, vendor_class_len);
2445
Simon Kelley7622fc02009-06-04 20:32:05 +01002446 /* restore BOOTP anti-overload hack */
Simon Kelley28866e92011-02-14 20:19:14 +00002447 if (!req_options || option_bool(OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00002448 {
2449 mess->file[0] = f0;
2450 mess->sname[0] = s0;
2451 }
2452}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002453
Simon Kelley7622fc02009-06-04 20:32:05 +01002454#endif
2455
2456
2457
2458
2459
2460
2461