blob: 9ac595e135b32bc20f4f0e8450f4c549ab869127 [file] [log] [blame]
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001/* dnsmasq is Copyright (c) 2000-2007 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
13 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
19#define BOOTREQUEST 1
20#define BOOTREPLY 2
21#define DHCP_COOKIE 0x63825363
22
Simon Kelleya2226412004-05-13 20:27:08 +010023/* The Linux in-kernel DHCP client silently ignores any packet
24 smaller than this. Sigh........... */
25#define MIN_PACKETSZ 300
26
Simon Kelley9e4abcb2004-01-22 19:47:41 +000027#define OPTION_PAD 0
28#define OPTION_NETMASK 1
29#define OPTION_ROUTER 3
30#define OPTION_DNSSERVER 6
31#define OPTION_HOSTNAME 12
32#define OPTION_DOMAINNAME 15
33#define OPTION_BROADCAST 28
Simon Kelley91dccd02005-03-31 17:48:32 +010034#define OPTION_VENDOR_CLASS_OPT 43
Simon Kelley9e4abcb2004-01-22 19:47:41 +000035#define OPTION_REQUESTED_IP 50
36#define OPTION_LEASE_TIME 51
37#define OPTION_OVERLOAD 52
38#define OPTION_MESSAGE_TYPE 53
39#define OPTION_SERVER_IDENTIFIER 54
40#define OPTION_REQUESTED_OPTIONS 55
Simon Kelley44a2a312004-03-10 20:04:35 +000041#define OPTION_MESSAGE 56
Simon Kelley9e4abcb2004-01-22 19:47:41 +000042#define OPTION_MAXMESSAGE 57
Simon Kelley44a2a312004-03-10 20:04:35 +000043#define OPTION_T1 58
44#define OPTION_T2 59
Simon Kelleya84fa1d2004-04-23 22:21:21 +010045#define OPTION_VENDOR_ID 60
Simon Kelley44a2a312004-03-10 20:04:35 +000046#define OPTION_CLIENT_ID 61
Simon Kelley1b7ecd12007-02-05 14:57:57 +000047#define OPTION_SNAME 66
48#define OPTION_FILENAME 67
Simon Kelleya2226412004-05-13 20:27:08 +010049#define OPTION_USER_CLASS 77
Simon Kelley3d8df262005-08-29 12:19:27 +010050#define OPTION_CLIENT_FQDN 81
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010051#define OPTION_AGENT_ID 82
Simon Kelley3be34542004-09-11 19:12:13 +010052#define OPTION_SUBNET_SELECT 118
Simon Kelley9e4abcb2004-01-22 19:47:41 +000053#define OPTION_END 255
54
Simon Kelleyf2621c72007-04-29 19:47:21 +010055#define SUBOPT_CIRCUIT_ID 1
56#define SUBOPT_REMOTE_ID 2
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010057#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
Simon Kelleyf2621c72007-04-29 19:47:21 +010058#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010059
Simon Kelley9e4abcb2004-01-22 19:47:41 +000060#define DHCPDISCOVER 1
61#define DHCPOFFER 2
62#define DHCPREQUEST 3
63#define DHCPDECLINE 4
64#define DHCPACK 5
65#define DHCPNAK 6
66#define DHCPRELEASE 7
67#define DHCPINFORM 8
68
Simon Kelley0a852542005-03-23 20:28:59 +000069#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010070#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
71#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
Simon Kelley0a852542005-03-23 20:28:59 +000072
Simon Kelleyf2621c72007-04-29 19:47:21 +010073static int sanitise(unsigned char *opt, char *buf);
Simon Kelley824af852008-02-12 20:43:05 +000074static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000075static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
76static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
77 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000078static struct in_addr option_addr(unsigned char *opt);
Simon Kelley44a2a312004-03-10 20:04:35 +000079static unsigned int option_uint(unsigned char *opt, int size);
Simon Kelley5aabfc72007-08-29 11:24:47 +010080static void log_packet(char *type, void *addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000081 unsigned char *ext_mac, int mac_len, char *interface, char *string);
Simon Kelleycdeda282006-03-16 20:16:06 +000082static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010083static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley5aabfc72007-08-29 11:24:47 +010084static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000085static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
86static void do_options(struct dhcp_context *context,
87 struct dhcp_packet *mess,
88 unsigned char *real_end,
89 unsigned char *req_options,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000090 char *hostname,
91 struct dhcp_netid *netid,
92 struct in_addr subnet_addr,
93 unsigned char fqdn_flags,
94 int null_term,
95 unsigned char *agent_id);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000096static unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +000097 int clid_len, unsigned char *clid, int *len_out);
98static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
99
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000100
Simon Kelley824af852008-02-12 20:43:05 +0000101size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100102 size_t sz, time_t now, int unicast_dest, int *is_inform)
Simon Kelley33820b72004-04-03 21:10:00 +0100103{
Simon Kelley26128d22004-11-14 16:43:54 +0000104 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000105 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +0100106 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +0000107 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +0000108 struct dhcp_netid_list *id_list;
Simon Kelley832af0b2007-01-21 20:01:28 +0000109 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000110 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000111 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley3d8df262005-08-29 12:19:27 +0100112 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000113 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100114 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +0000115 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000116 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000117 struct dhcp_config *config;
Simon Kelleya2226412004-05-13 20:27:08 +0100118 struct dhcp_netid *netid = NULL;
Simon Kelleyaedef832006-01-22 14:02:31 +0000119 struct in_addr subnet_addr, fallback;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100120 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100121 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100122 unsigned char fqdn_flags = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100123 unsigned char *agent_id = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000124 unsigned char *emac = NULL;
125 int emac_len;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100126 struct dhcp_netid known_id;
Simon Kelley3be34542004-09-11 19:12:13 +0100127
Simon Kelley849a8352006-06-09 21:02:31 +0100128 subnet_addr.s_addr = 0;
129
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100130 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100131 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000132
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100133 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000134 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100135
Simon Kelley3be34542004-09-11 19:12:13 +0100136 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000137 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000138 {
Simon Kelley3be34542004-09-11 19:12:13 +0100139 mess_type = option_uint(opt, 1);
Simon Kelley44a2a312004-03-10 20:04:35 +0000140
Simon Kelley3be34542004-09-11 19:12:13 +0100141 /* only insist on a cookie for DHCP. */
142 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
143 return 0;
144
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100145 /* two things to note here: expand_buf may move the packet,
146 so reassign mess from daemon->packet. Also, the size
147 sent includes the IP and UDP headers, hence the magic "-28" */
148 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
149 {
150 size_t size = (size_t)option_uint(opt, 2) - 28;
151
152 if (size > DHCP_PACKET_MAX)
153 size = DHCP_PACKET_MAX;
154 else if (size < sizeof(struct dhcp_packet))
155 size = sizeof(struct dhcp_packet);
156
157 if (expand_buf(&daemon->dhcp_packet, size))
158 {
Simon Kelley824af852008-02-12 20:43:05 +0000159 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100160 end = ((unsigned char *)mess) + size;
161 }
162 }
163
Simon Kelley3be34542004-09-11 19:12:13 +0100164 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
165 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000166 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100167 mess->ciaddr.s_addr = 0;
168
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100169 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
170 {
171 /* Any agent-id needs to be copied back out, verbatim, as the last option
172 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
173 get overwritten, then it will be shuffled back at the end of processing.
174 Note that the incoming options must not be overwritten here, so there has to
175 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100176 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100177 unsigned int total = option_len(opt) + 2;
178 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
179 if (last_opt && last_opt < end - total)
180 {
181 agent_id = end - total;
182 memcpy(agent_id, opt, total);
183 }
184
185 /* look for RFC3527 Link selection sub-option */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100186 if ((sopt = option_find1(option_ptr(opt), option_ptr(opt) + option_len(opt), SUBOPT_SUBNET_SELECT, INADDRSZ)))
187 subnet_addr = option_addr(sopt);
188
189 /* if a circuit-id or remote-is option is provided, exact-match to options. */
190 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
191 {
192 int search;
193
194 if (vendor->match_type == MATCH_CIRCUIT)
195 search = SUBOPT_CIRCUIT_ID;
196 else if (vendor->match_type == MATCH_REMOTE)
197 search = SUBOPT_REMOTE_ID;
198 else if (vendor->match_type == MATCH_SUBSCRIBER)
199 search = SUBOPT_SUBSCR_ID;
200 else
201 continue;
202
203 if ((sopt = option_find1(option_ptr(opt), option_ptr(opt) + option_len(opt), search, 1)) &&
204 vendor->len == option_len(sopt) &&
205 memcmp(option_ptr(sopt), vendor->data, vendor->len) == 0)
206 {
207 vendor->netid.next = netid;
208 netid = &vendor->netid;
209 break;
210 }
211 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100212 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100213
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100214 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
215 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100216 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000217
218 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000219 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000220 {
Simon Kelley26128d22004-11-14 16:43:54 +0000221 clid_len = option_len(opt);
Simon Kelleybb01cb92004-12-13 20:56:23 +0000222 clid = option_ptr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000223 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000224
Simon Kelley0a852542005-03-23 20:28:59 +0000225 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100226 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000227
228 /* If this request is missing a clid, but we've seen one before,
229 use it again for option matching etc. */
230 if (lease && !clid && lease->clid)
231 {
232 clid_len = lease->clid_len;
233 clid = lease->clid;
234 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000235
236 /* find mac to use for logging and hashing */
237 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000238 }
Simon Kelley3be34542004-09-11 19:12:13 +0100239
Simon Kelleycdeda282006-03-16 20:16:06 +0000240 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
241 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100242 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
243 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000244 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100245 mac->netid.next = netid;
246 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000247 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100248
Simon Kelley0a852542005-03-23 20:28:59 +0000249 /* Determine network for this packet. Our caller will have already linked all the
250 contexts which match the addresses of the receiving interface but if the
251 machine has an address already, or came via a relay, or we have a subnet selector,
252 we search again. If we don't have have a giaddr or explicit subnet selector,
253 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100254 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
255 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000256 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
257 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100258 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100259 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100260 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000261
Simon Kelley3d8df262005-08-29 12:19:27 +0100262 if (subnet_addr.s_addr)
263 {
264 addr = subnet_addr;
265 force = 1;
266 }
267 else if (mess->giaddr.s_addr)
268 {
269 addr = mess->giaddr;
270 force = 1;
271 }
Simon Kelley16972692006-10-16 20:04:18 +0100272 else
273 {
274 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
275 addr = mess->ciaddr;
276 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
277 if (context_tmp->netmask.s_addr &&
278 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
279 is_same_net(addr, context_tmp->end, context_tmp->netmask))
280 {
281 context_new = context;
282 break;
283 }
284 }
285
286 if (!context_new)
287 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
288 if (context_tmp->netmask.s_addr &&
289 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
290 is_same_net(addr, context_tmp->end, context_tmp->netmask))
291 {
292 context_tmp->current = context_new;
293 context_new = context_tmp;
294 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100295
Simon Kelley3d8df262005-08-29 12:19:27 +0100296 if (context_new || force)
297 context = context_new;
Simon Kelley16972692006-10-16 20:04:18 +0100298
Simon Kelley0a852542005-03-23 20:28:59 +0000299 }
Simon Kelley3be34542004-09-11 19:12:13 +0100300
301 if (!context)
302 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100303 my_syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"),
304 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
305 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 +0100306 return 0;
307 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100308
Simon Kelleyaedef832006-01-22 14:02:31 +0000309 /* keep _a_ local address available. */
310 fallback = context->local;
311
Simon Kelleyf2621c72007-04-29 19:47:21 +0100312 if (daemon->options & OPT_LOG_OPTS)
313 {
314 struct dhcp_context *context_tmp;
315 my_syslog(LOG_INFO, _("DHCP packet: transaction-id is %u"), mess->xid);
316 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
317 {
318 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
319 if (context_tmp->flags & CONTEXT_STATIC)
320 my_syslog(LOG_INFO, _("Available DHCP subnet: %s/%s"), daemon->namebuff, inet_ntoa(context_tmp->netmask));
321 else
322 my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
323 }
324 }
325
Simon Kelley3be34542004-09-11 19:12:13 +0100326 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100327
Simon Kelleycdeda282006-03-16 20:16:06 +0000328 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
329 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100330
331 /* set "known" tag for known hosts */
332 if (config)
333 {
334 known_id.net = "known";
335 known_id.next = netid;
336 netid = &known_id;
337 }
Simon Kelley26128d22004-11-14 16:43:54 +0000338
Simon Kelley3be34542004-09-11 19:12:13 +0100339 if (mess_type == 0)
340 {
341 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000342 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000343 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100344
345 /* must have a MAC addr for bootp */
346 if (mess->htype == 0 || mess->hlen == 0)
347 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000348
Simon Kelley26128d22004-11-14 16:43:54 +0000349 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000350 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000351
352 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
353
354 if (have_config(config, CONFIG_NAME))
355 hostname = config->hostname;
356
357 if (have_config(config, CONFIG_NETID))
358 {
359 config->netid.next = netid;
360 netid = &config->netid;
361 }
362
363 /* Match incoming filename field as a netid. */
364 if (mess->file[0])
365 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000366 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
367 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
368 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000369 id.next = netid;
370 netid = &id;
371 }
Simon Kelley6b010842007-02-12 20:32:07 +0000372
373 /* Add "bootp" as a tag to allow different options, address ranges etc
374 for BOOTP clients */
375 bootp_id.net = "bootp";
376 bootp_id.next = netid;
377 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000378
379 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000380 if (match_netid(id_list->list, netid, 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000381 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000382
Simon Kelley3d8df262005-08-29 12:19:27 +0100383 if (!message)
384 {
385 if (have_config(config, CONFIG_ADDR))
386 {
387 logaddr = &config->addr;
388 mess->yiaddr = config->addr;
389 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000390 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100391 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000392 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000393 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100394 }
395 else if (!(daemon->options & OPT_BOOTP_DYNAMIC))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000396 message = _("no address configured");
Simon Kelley3d8df262005-08-29 12:19:27 +0100397 else
398 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000399 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley824af852008-02-12 20:43:05 +0000400 !address_available(context, lease->addr, netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000401 {
402 if (lease)
403 {
404 /* lease exists, wrong network. */
405 lease_prune(lease, now);
406 lease = NULL;
407 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100408 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000409 message = _("no address available");
410 }
411 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100412 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100413 }
414
Simon Kelley7cebd202006-05-06 14:13:33 +0100415 if (!message &&
416 !lease &&
417 (!(lease = lease_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000418 message = _("no leases left");
419
420 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
Simon Kelleye17fb622006-01-14 20:33:46 +0000421 message = _("wrong network");
422
Simon Kelley3d8df262005-08-29 12:19:27 +0100423 if (!message)
424 {
425 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100426
Simon Kelleycdeda282006-03-16 20:16:06 +0000427 if (context->netid.net)
Simon Kelley3d8df262005-08-29 12:19:27 +0100428 {
429 context->netid.next = netid;
430 netid = &context->netid;
431 }
432
Simon Kelleycdeda282006-03-16 20:16:06 +0000433 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100434 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000435 lease_set_hostname(lease, hostname, daemon->domain_suffix, 1);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100436 lease_set_expires(lease, 0xffffffff, now); /* infinite lease */
Simon Kelley824af852008-02-12 20:43:05 +0000437 lease_set_interface(lease, int_index);
Simon Kelley3d8df262005-08-29 12:19:27 +0100438
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000439 clear_packet(mess, end);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100440 do_options(context, mess, end, NULL,
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000441 hostname, netid, subnet_addr, 0, 0, NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100442 }
443 }
444
Simon Kelley5aabfc72007-08-29 11:24:47 +0100445 log_packet(NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000446
Simon Kelley5aabfc72007-08-29 11:24:47 +0100447 return message ? 0 : dhcp_packet_size(mess, netid);
Simon Kelley3be34542004-09-11 19:12:13 +0100448 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000449
Simon Kelley3d8df262005-08-29 12:19:27 +0100450 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
451 {
452 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
453 int len = option_len(opt);
454 char *pq = daemon->dhcp_buff;
455 unsigned char *pp, *op = option_ptr(opt);
456
457 fqdn_flags = *op;
458 len -= 3;
459 op += 3;
460 pp = op;
461
462 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000463 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100464 fqdn_flags |= 0x02;
465
466 fqdn_flags &= ~0x08;
467 fqdn_flags |= 0x01;
468
469 if (fqdn_flags & 0x04)
470 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
471 {
472 memcpy(pq, op+1, *op);
473 pq += *op;
474 op += (*op)+1;
475 *(pq++) = '.';
476 }
477 else
478 {
479 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000480 if (len > 0 && op[len-1] == 0)
481 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100482 pq += len + 1;
483 }
484
485 if (pq != daemon->dhcp_buff)
486 pq--;
487
488 *pq = 0;
489
490 if (canonicalise(daemon->dhcp_buff))
491 offer_hostname = client_hostname = daemon->dhcp_buff;
492 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000493 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000494 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000495 int len = option_len(opt);
Simon Kelley3d8df262005-08-29 12:19:27 +0100496 memcpy(daemon->dhcp_buff, option_ptr(opt), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000497 /* Microsoft clients are broken, and need zero-terminated strings
498 in options. We detect this state here, and do the same in
499 any options we send */
500 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
501 borken_opt = 1;
502 else
503 daemon->dhcp_buff[len] = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100504 if (canonicalise(daemon->dhcp_buff))
505 client_hostname = daemon->dhcp_buff;
506 }
507
508 if (have_config(config, CONFIG_NAME))
509 {
510 hostname = config->hostname;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000511 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000512 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100513 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000514 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100515 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100516 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100517 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100518 char *d = strip_hostname(client_hostname);
519 if (d)
520 my_syslog(LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), d, client_hostname);
521
522 if (strlen(client_hostname) != 0)
523 {
524 hostname = client_hostname;
525 if (!config)
526 {
527 /* Search again now we have a hostname.
528 Only accept configs without CLID and HWADDR here, (they won't match)
529 to avoid impersonation by name. */
530 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
531 mess->chaddr, mess->hlen,
532 mess->htype, hostname);
Simon Kelley824af852008-02-12 20:43:05 +0000533 if (new && !have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
534 {
535 config = new;
536 /* set "known" tag for known hosts */
537 known_id.net = "known";
538 known_id.next = netid;
539 netid = &known_id;
540 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100541 }
542 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000543 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100544
Simon Kelleya2226412004-05-13 20:27:08 +0100545 if (have_config(config, CONFIG_NETID))
546 {
547 config->netid.next = netid;
548 netid = &config->netid;
549 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100550
Simon Kelley26128d22004-11-14 16:43:54 +0000551 /* user-class options are, according to RFC3004, supposed to contain
552 a set of counted strings. Here we check that this is so (by seeing
553 if the counts are consistent with the overall option length) and if
554 so zero the counts so that we don't get spurious matches between
555 the vendor string and the counts. If the lengths don't add up, we
556 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100557 and just do the substring match. dhclient provides these broken options.
558 The code, later, which sends user-class data to the lease-change script
559 relies on the transformation done here.
560 */
Simon Kelleya2226412004-05-13 20:27:08 +0100561
Simon Kelleybb01cb92004-12-13 20:56:23 +0000562 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100563 {
Simon Kelley26128d22004-11-14 16:43:54 +0000564 unsigned char *ucp = option_ptr(opt);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000565 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000566 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
567 if (j == option_len(opt))
568 for (j = 0; j < option_len(opt); j = tmp)
569 {
570 tmp = j + ucp[j] + 1;
571 ucp[j] = 0;
572 }
Simon Kelleya2226412004-05-13 20:27:08 +0100573 }
Simon Kelley26128d22004-11-14 16:43:54 +0000574
575 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100576 {
577 int mopt;
578
579 if (vendor->match_type == MATCH_VENDOR)
580 mopt = OPTION_VENDOR_ID;
581 else if (vendor->match_type == MATCH_USER)
582 mopt = OPTION_USER_CLASS;
Simon Kelley824af852008-02-12 20:43:05 +0000583 else if (vendor->match_type == MATCH_OPTION)
584 {
585 if (option_find(mess, sz, vendor->option, 1))
586 {
587 vendor->netid.next = netid;
588 netid = &vendor->netid;
589 }
590 continue;
591 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100592 else
593 continue;
594
595 if ((opt = option_find(mess, sz, mopt, 1)))
596 {
597 int i;
598 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
599 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
600 {
601 vendor->netid.next = netid;
602 netid = &vendor->netid;
603 break;
604 }
605 }
606 }
607
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000608 /* mark vendor-encapsulated options which match the client-supplied vendor class */
Simon Kelley6b010842007-02-12 20:32:07 +0000609 match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000610
Simon Kelleyf2621c72007-04-29 19:47:21 +0100611 if (daemon->options & OPT_LOG_OPTS)
612 {
613 if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
614 my_syslog(LOG_INFO, _("Vendor class: %s"), daemon->namebuff);
615 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
616 my_syslog(LOG_INFO, _("User class: %s"), daemon->namebuff);
617 }
618
Simon Kelley26128d22004-11-14 16:43:54 +0000619 /* if all the netids in the ignore list are present, ignore this client */
620 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000621 if (match_netid(id_list->list, netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000622 ignore = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000623
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100624 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
625 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000626 clid = NULL;
627
Simon Kelleybb01cb92004-12-13 20:56:23 +0000628 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100629 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100630 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000631 memcpy(req_options, option_ptr(opt), option_len(opt));
632 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100633 }
634
Simon Kelley3be34542004-09-11 19:12:13 +0100635 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000636 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000637 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000638 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000639 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000640 return 0;
641
642 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100643 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000644
Simon Kelleybb01cb92004-12-13 20:56:23 +0000645 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000646 return 0;
647
Simon Kelley5aabfc72007-08-29 11:24:47 +0100648 log_packet("DECLINE", option_ptr(opt), emac, emac_len, iface_name, daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000649
650 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
651 lease_prune(lease, now);
652
Simon Kelley33820b72004-04-03 21:10:00 +0100653 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000654 config->addr.s_addr == option_addr(opt).s_addr)
655 {
Simon Kelley849a8352006-06-09 21:02:31 +0100656 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100657 my_syslog(LOG_WARNING, _("disabling DHCP static address %s for %s"),
658 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100659 config->flags |= CONFIG_DECLINED;
660 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000661 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100662 else
663 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100664 for (; context; context = context->current)
665 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000666
667 return 0;
668
669 case DHCPRELEASE:
Simon Kelley824af852008-02-12 20:43:05 +0000670 if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100671 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley0a852542005-03-23 20:28:59 +0000672 (context->local.s_addr != option_addr(opt).s_addr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000673 return 0;
674
Simon Kelley44a2a312004-03-10 20:04:35 +0000675 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
676 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100677 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000678 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100679
Simon Kelley5aabfc72007-08-29 11:24:47 +0100680 log_packet("RELEASE", &mess->ciaddr, emac, emac_len, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000681
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000682 return 0;
683
684 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100685 if (ignore || have_config(config, CONFIG_DISABLE))
686 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000687 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100688 opt = NULL;
689 }
690 else
691 {
692 struct in_addr addr, conf;
693
694 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
695 addr = option_addr(opt);
696
697 conf.s_addr = 0;
698 if (have_config(config, CONFIG_ADDR))
699 {
Simon Kelley849a8352006-06-09 21:02:31 +0100700 char *addrs = inet_ntoa(config->addr);
701
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100702 if ((ltmp = lease_find_by_addr(config->addr)) && ltmp != lease)
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000703 {
704 int len;
705 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
706 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100707 my_syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100708 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000709 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100710 else
711 {
712 struct dhcp_context *tmp;
713 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100714 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100715 break;
716 if (tmp)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100717 my_syslog(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 +0100718 else if (have_config(config, CONFIG_DECLINED) &&
719 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100720 my_syslog(LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100721 else
722 conf = config->addr;
723 }
724 }
725
726 if (conf.s_addr)
727 mess->yiaddr = conf;
Simon Kelley824af852008-02-12 20:43:05 +0000728 else if (lease && address_available(context, lease->addr, netid))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100729 mess->yiaddr = lease->addr;
Simon Kelley824af852008-02-12 20:43:05 +0000730 else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100731 !config_find_by_address(daemon->dhcp_conf, addr))
732 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100733 else if (emac_len == 0)
734 message = _("no unique-id");
735 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100736 message = _("no address available");
737 }
738
Simon Kelley5aabfc72007-08-29 11:24:47 +0100739 log_packet("DISCOVER", opt ? option_ptr(opt) : NULL, emac, emac_len, iface_name, message);
Simon Kelley3d8df262005-08-29 12:19:27 +0100740
Simon Kelley824af852008-02-12 20:43:05 +0000741 if (message || !(context = narrow_context(context, mess->yiaddr, netid)))
Simon Kelley33820b72004-04-03 21:10:00 +0100742 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +0000743
Simon Kelley5aabfc72007-08-29 11:24:47 +0100744 log_packet("OFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000745
Simon Kelleycdeda282006-03-16 20:16:06 +0000746 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000747 {
748 context->netid.next = netid;
749 netid = &context->netid;
750 }
751
Simon Kelley824af852008-02-12 20:43:05 +0000752 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000753 clear_packet(mess, end);
754 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
755 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
756 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100757 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +0000758 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100759 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000760 option_put(mess, end, OPTION_T1, 4, (time/2));
761 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100762 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100763 do_options(context, mess, end, req_options, offer_hostname,
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000764 netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000765
Simon Kelley5aabfc72007-08-29 11:24:47 +0100766 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000767
768 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +0000769 if (ignore || have_config(config, CONFIG_DISABLE))
770 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000771 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000772 {
773 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000774 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000775
Simon Kelley4011c4e2006-10-28 16:26:19 +0100776 /* send vendor and user class info for new or recreated lease */
777 do_classes = 1;
778
Simon Kelleybb01cb92004-12-13 20:56:23 +0000779 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000780 {
Simon Kelley3be34542004-09-11 19:12:13 +0100781 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +0000782 selecting = 1;
783
Simon Kelleye17fb622006-01-14 20:33:46 +0000784 for (; context; context = context->current)
785 if (context->local.s_addr == option_addr(opt).s_addr)
786 break;
787
788 if (!context)
Simon Kelley3be34542004-09-11 19:12:13 +0100789 return 0;
790
791 /* If a lease exists for this host and another address, squash it. */
792 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
793 {
794 lease_prune(lease, now);
795 lease = NULL;
796 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000797 }
Simon Kelley3be34542004-09-11 19:12:13 +0100798 else
799 {
800 /* INIT-REBOOT */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100801 if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +0100802 return 0;
803
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100804 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley824af852008-02-12 20:43:05 +0000805 {
806 message = _("wrong address");
807 /* avoid loops when client brain-dead */
808 lease_prune(lease, now);
809 lease = NULL;
810 }
Simon Kelley3be34542004-09-11 19:12:13 +0100811 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000812 }
813 else
814 {
815 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +0000816 /* Check existing lease for this address.
817 We allow it to be missing if dhcp-authoritative mode
818 as long as we can allocate the lease now - checked below.
819 This makes for a smooth recovery from a lost lease DB */
820 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
821 (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000822 {
823 message = _("lease not found");
824 /* ensure we broadcast NAK */
825 unicast_dest = 0;
826 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100827 /* desynchronise renewals */
828 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +0100829 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000830 }
831
Simon Kelley5aabfc72007-08-29 11:24:47 +0100832 log_packet("REQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL);
Simon Kelleye17fb622006-01-14 20:33:46 +0000833
Simon Kelleydfa666f2004-08-02 18:27:27 +0100834 if (!message)
835 {
836 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100837 struct dhcp_context *tmp = NULL;
838
839 if (have_config(config, CONFIG_ADDR))
840 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100841 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100842 break;
Simon Kelleyaedef832006-01-22 14:02:31 +0000843
Simon Kelley824af852008-02-12 20:43:05 +0000844 if (!(context = narrow_context(context, mess->yiaddr, netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000845 {
Simon Kelleye17fb622006-01-14 20:33:46 +0000846 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000847 message = _("wrong network");
848 /* ensure we broadcast NAK */
849 unicast_dest = 0;
850 }
851
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100852 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley824af852008-02-12 20:43:05 +0000853 else if (!address_available(context, mess->yiaddr, netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +0100854 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000855 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +0000856
Simon Kelleydfa666f2004-08-02 18:27:27 +0100857 /* Check if a new static address has been configured. Be very sure that
858 when the client does DISCOVER, it will get the static address, otherwise
859 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +0000860 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100861 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +0100862 (!have_config(config, CONFIG_DECLINED) ||
863 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100864 config->addr.s_addr != mess->yiaddr.s_addr &&
865 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000866 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100867
868 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100869 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000870 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100871
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100872 else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000873 message = _("address in use");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100874
Simon Kelley5aabfc72007-08-29 11:24:47 +0100875 else if (emac_len == 0)
Simon Kelley7cebd202006-05-06 14:13:33 +0100876 message = _("no unique-id");
877
Simon Kelley16972692006-10-16 20:04:18 +0100878 else if (!lease)
879 {
Simon Kelley4011c4e2006-10-28 16:26:19 +0100880 if ((lease = lease_allocate(mess->yiaddr)))
881 do_classes = 1;
Simon Kelley16972692006-10-16 20:04:18 +0100882 else
Simon Kelley4011c4e2006-10-28 16:26:19 +0100883 message = _("no leases left");
Simon Kelley16972692006-10-16 20:04:18 +0100884 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100885 }
Simon Kelley16972692006-10-16 20:04:18 +0100886
Simon Kelley44a2a312004-03-10 20:04:35 +0000887 if (message)
888 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100889 log_packet("NAK", &mess->yiaddr, emac, emac_len, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000890
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000891 mess->yiaddr.s_addr = 0;
892 clear_packet(mess, end);
893 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
894 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ,
895 ntohl(context ? context->local.s_addr : fallback.s_addr));
896 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000897 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
898 a distant subnet which unicast a REQ to us won't work. */
899 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
900 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
901 {
902 mess->flags |= htons(0x8000); /* broadcast */
903 mess->ciaddr.s_addr = 0;
904 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000905 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100906 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000907 {
Simon Kelley4011c4e2006-10-28 16:26:19 +0100908 if (do_classes)
909 {
910 lease->changed = 1;
911 /* copy user-class and vendor class into new lease, for the script */
912 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
913 {
914 int len = option_len(opt);
915 unsigned char *ucp = option_ptr(opt);
916 /* If the user-class option started as counted strings, the first byte will be zero. */
917 if (len != 0 && ucp[0] == 0)
918 ucp++, len--;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100919 free(lease->userclass);
920 if ((lease->userclass = whine_malloc(len+1)))
Simon Kelley4011c4e2006-10-28 16:26:19 +0100921 {
922 memcpy(lease->userclass, ucp, len);
923 lease->userclass[len] = 0;
924 lease->userclass_len = len+1;
925 }
926 }
927 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
928 {
929 int len = option_len(opt);
930 unsigned char *ucp = option_ptr(opt);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100931 free(lease->vendorclass);
932 if ((lease->vendorclass = whine_malloc(len+1)))
Simon Kelley4011c4e2006-10-28 16:26:19 +0100933 {
934 memcpy(lease->vendorclass, ucp, len);
935 lease->vendorclass[len] = 0;
936 lease->vendorclass_len = len+1;
937 }
938 }
939 }
940
Simon Kelley5aabfc72007-08-29 11:24:47 +0100941 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
Simon Kelley4011c4e2006-10-28 16:26:19 +0100942 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000943 hostname = client_hostname;
944 hostname_auth = 1;
945 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100946
Simon Kelleycdeda282006-03-16 20:16:06 +0000947 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000948 {
949 context->netid.next = netid;
950 netid = &context->netid;
951 }
952
Simon Kelley824af852008-02-12 20:43:05 +0000953 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +0000954 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley832af0b2007-01-21 20:01:28 +0000955
956 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
957 if (!hostname_auth)
958 {
959 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
960 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
961 break;
962 if (id_list)
963 hostname = NULL;
964 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100965 if (hostname)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000966 lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
Simon Kelley832af0b2007-01-21 20:01:28 +0000967
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100968 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +0000969 lease_set_interface(lease, int_index);
970
Simon Kelley5aabfc72007-08-29 11:24:47 +0100971 log_packet("ACK", &mess->yiaddr, emac, emac_len, iface_name, hostname);
Simon Kelley832af0b2007-01-21 20:01:28 +0000972
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000973 clear_packet(mess, end);
974 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
975 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
976 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +0000977 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100978 {
Simon Kelley59353a62004-11-21 19:34:28 +0000979 while (fuzz > (time/16))
980 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000981 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
982 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100983 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100984 do_options(context, mess, end, req_options, hostname,
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000985 netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley44a2a312004-03-10 20:04:35 +0000986 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100987
Simon Kelley5aabfc72007-08-29 11:24:47 +0100988 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000989
990 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +0000991 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000992 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +0100993
Simon Kelley5aabfc72007-08-29 11:24:47 +0100994 log_packet("INFORM", &mess->ciaddr, emac, emac_len, iface_name, message);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100995
Simon Kelleye17fb622006-01-14 20:33:46 +0000996 if (message || mess->ciaddr.s_addr == 0 ||
Simon Kelley824af852008-02-12 20:43:05 +0000997 !(context = narrow_context(context, mess->ciaddr, netid)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100998 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000999
Simon Kelley5aabfc72007-08-29 11:24:47 +01001000 /* Find a least based on IP address if we didn't
1001 get one from MAC address/client-d */
1002 if (!lease &&
1003 (lease = lease_find_by_addr(mess->ciaddr)) &&
1004 lease->hostname)
1005 hostname = lease->hostname;
1006
1007 if (!hostname)
1008 hostname = host_from_dns(mess->ciaddr);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001009
Simon Kelley5aabfc72007-08-29 11:24:47 +01001010 log_packet("ACK", &mess->ciaddr, emac, emac_len, iface_name, hostname);
1011
Simon Kelley59353a62004-11-21 19:34:28 +00001012 if (context->netid.net)
1013 {
1014 context->netid.next = netid;
1015 netid = &context->netid;
1016 }
1017
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001018 clear_packet(mess, end);
1019 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
1020 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley5aabfc72007-08-29 11:24:47 +01001021
1022 if (lease)
1023 {
1024 if (lease->expires == 0)
1025 time = 0xffffffff;
1026 else
1027 time = (unsigned int)difftime(lease->expires, now);
1028 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001029 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001030 }
Simon Kelley824af852008-02-12 20:43:05 +00001031
Simon Kelley5aabfc72007-08-29 11:24:47 +01001032 do_options(context, mess, end, req_options, hostname,
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001033 netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001034
Simon Kelley5aabfc72007-08-29 11:24:47 +01001035 *is_inform = 1; /* handle reply differently */
1036 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001037 }
1038
1039 return 0;
1040}
1041
Simon Kelley6b010842007-02-12 20:32:07 +00001042/* find a good value to use as MAC address for logging and address-allocation hashing.
1043 This is normally just the chaddr field from the DHCP packet,
1044 but eg Firewire will have hlen == 0 and use the client-id instead.
1045 This could be anything, but will normally be EUI64 for Firewire.
1046 We assume that if the first byte of the client-id equals the htype byte
1047 then the client-id is using the usual encoding and use the rest of the
1048 client-id: if not we can use the whole client-id. This should give
1049 sane MAC address logs. */
1050static unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
1051 int clid_len, unsigned char *clid, int *len_out)
1052{
1053 if (hwlen == 0 && clid && clid_len > 3)
1054 {
1055 if (clid[0] == hwtype)
1056 {
1057 *len_out = clid_len - 1 ;
1058 return clid + 1;
1059 }
1060
1061#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1062 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1063 {
1064 *len_out = clid_len - 1 ;
1065 return clid + 1;
1066 }
1067#endif
1068
1069 *len_out = clid_len;
1070 return clid;
1071 }
1072
1073 *len_out = hwlen;
1074 return hwaddr;
1075}
1076
Simon Kelley824af852008-02-12 20:43:05 +00001077static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001078{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001079 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001080
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001081 if (opt)
1082 {
1083 unsigned int req_time = option_uint(opt, 4);
1084 if (req_time < 120 )
1085 req_time = 120; /* sanity */
1086 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1087 time = req_time;
1088 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001089
1090 return time;
1091}
1092
Simon Kelleyf2621c72007-04-29 19:47:21 +01001093static int sanitise(unsigned char *opt, char *buf)
1094{
1095 char *p;
1096 int i;
1097
1098 *buf = 0;
1099
1100 if (!opt)
1101 return 0;
1102
1103 p = option_ptr(opt);
1104
1105 for (i = option_len(opt); i > 0; i--)
1106 {
1107 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001108 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001109 *buf++ = c;
1110 }
1111 *buf = 0; /* add terminator */
1112
1113 return 1;
1114}
1115
Simon Kelley5aabfc72007-08-29 11:24:47 +01001116static void log_packet(char *type, void *addr, unsigned char *ext_mac,
1117 int mac_len, char *interface, char *string)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001118{
Simon Kelley16972692006-10-16 20:04:18 +01001119 struct in_addr a;
1120
1121 /* addr may be misaligned */
1122 if (addr)
1123 memcpy(&a, addr, sizeof(a));
1124
Simon Kelleyf2621c72007-04-29 19:47:21 +01001125 my_syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
1126 type ? "DHCP" : "BOOTP",
1127 type ? type : "",
1128 interface,
1129 addr ? inet_ntoa(a) : "",
1130 addr ? " " : "",
Simon Kelley5aabfc72007-08-29 11:24:47 +01001131 print_mac(daemon->namebuff, ext_mac, mac_len),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001132 string ? string : "");
1133}
1134
Simon Kelley5aabfc72007-08-29 11:24:47 +01001135static void log_options(unsigned char *start)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001136{
1137 while (*start != OPTION_END)
1138 {
1139 char *text = option_string(start[0]);
1140 unsigned char trunc = start[1] < 13 ? start[1] : 13;
1141 my_syslog(LOG_INFO, "sent size:%3d option:%3d%s%s%s%s%s",
1142 start[1], start[0],
1143 text ? ":" : "", text ? text : "",
1144 start[1] == 0 ? "" : " ",
Simon Kelley5aabfc72007-08-29 11:24:47 +01001145 start[1] == 0 ? "" : print_mac(daemon->namebuff, &start[2], trunc),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001146 trunc == start[1] ? "" : "...");
1147 start += start[1] + 2;
1148 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001149}
1150
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001151static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001152{
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001153 while (*p != OPTION_END)
1154 {
Simon Kelleybb01cb92004-12-13 20:56:23 +00001155 if (p >= end)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001156 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001157 else if (*p == OPTION_PAD)
1158 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001159 else
1160 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001161 int opt_len;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001162 if (p >= end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001163 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001164 opt_len = option_len(p);
Simon Kelleybb01cb92004-12-13 20:56:23 +00001165 if (p >= end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001166 return NULL; /* malformed packet */
1167 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001168 return p;
1169 p += opt_len + 2;
1170 }
1171 }
1172
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001173 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001174}
1175
Simon Kelleycdeda282006-03-16 20:16:06 +00001176static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001177{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001178 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001179
Simon Kelley3be34542004-09-11 19:12:13 +01001180 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001181 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1182 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001183
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001184 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001185 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001186 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001187
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001188 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001189 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001190 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1191 return ret;
1192
1193 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001194 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001195 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1196 return ret;
1197
1198 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001199}
1200
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001201static struct in_addr option_addr(unsigned char *opt)
1202{
1203 /* this worries about unaligned data in the option. */
1204 /* struct in_addr is network byte order */
1205 struct in_addr ret;
1206
1207 memcpy(&ret, option_ptr(opt), INADDRSZ);
1208
1209 return ret;
1210}
1211
1212static unsigned int option_uint(unsigned char *opt, int size)
1213{
1214 /* this worries about unaligned data and byte order */
1215 unsigned int ret = 0;
1216 int i;
1217 unsigned char *p = option_ptr(opt);
1218
1219 for (i = 0; i < size; i++)
1220 ret = (ret << 8) | *p++;
1221
1222 return ret;
1223}
1224
1225static unsigned char *dhcp_skip_opts(unsigned char *start)
1226{
1227 while (*start != 0)
1228 start += start[1] + 2;
1229 return start;
1230}
1231
1232/* only for use when building packet: doesn't check for bad data. */
1233static unsigned char *find_overload(struct dhcp_packet *mess)
1234{
1235 unsigned char *p = &mess->options[0] + sizeof(u32);
1236
1237 while (*p != 0)
1238 {
1239 if (*p == OPTION_OVERLOAD)
1240 return p;
1241 p += p[1] + 2;
1242 }
1243 return NULL;
1244}
1245
Simon Kelley5aabfc72007-08-29 11:24:47 +01001246static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001247{
1248 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1249 unsigned char *overload;
1250 size_t ret;
Simon Kelley824af852008-02-12 20:43:05 +00001251 struct dhcp_netid_list *id_list;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001252
1253 /* We do logging too */
1254 if (netid && (daemon->options & OPT_LOG_OPTS))
1255 {
1256 char *p = daemon->namebuff;
1257 *p = 0;
1258 for (; netid; netid = netid->next)
1259 {
1260 strncat (p, netid->net, MAXDNAME);
1261 if (netid->next)
1262 strncat (p, ", ", MAXDNAME);
1263 }
1264 p[MAXDNAME - 1] = 0;
1265 my_syslog(LOG_INFO, _("tags: %s"), p);
1266 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001267
1268 /* add END options to the regions. */
1269 if ((overload = find_overload(mess)))
1270 {
1271 if (option_uint(overload, 1) & 1)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001272 {
1273 *dhcp_skip_opts(mess->file) = OPTION_END;
1274 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001275 log_options(mess->file);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001276 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001277 if (option_uint(overload, 1) & 2)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001278 {
1279 *dhcp_skip_opts(mess->sname) = OPTION_END;
1280 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001281 log_options(mess->sname);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001282 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001283 }
1284
1285 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001286
Simon Kelleyf2621c72007-04-29 19:47:21 +01001287 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001288 log_options(&mess->options[0] + sizeof(u32));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001289
Simon Kelley824af852008-02-12 20:43:05 +00001290 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
1291 if (match_netid(id_list->list, netid, 0))
1292 mess->flags |= htons(0x8000); /* force broadcast */
1293
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001294 ret = (size_t)(p - (unsigned char *)mess);
1295
1296 if (ret < MIN_PACKETSZ)
1297 ret = MIN_PACKETSZ;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001298
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001299 return ret;
1300}
1301
1302static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1303{
1304 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1305
1306 if (p + len + 3 >= end)
1307 /* not enough space in options area, try and use overload, if poss */
1308 {
1309 unsigned char *overload;
1310
1311 if (!(overload = find_overload(mess)) &&
1312 (mess->file[0] == 0 || mess->sname[0] == 0))
1313 {
1314 /* attempt to overload fname and sname areas, we've reserved space for the
1315 overflow option previuously. */
1316 overload = p;
1317 *(p++) = OPTION_OVERLOAD;
1318 *(p++) = 1;
1319 }
1320
1321 p = NULL;
1322
1323 /* using filename field ? */
1324 if (overload)
1325 {
1326 if (mess->file[0] == 0)
1327 overload[2] |= 1;
1328
1329 if (overload[2] & 1)
1330 {
1331 p = dhcp_skip_opts(mess->file);
1332 if (p + len + 3 >= mess->file + sizeof(mess->file))
1333 p = NULL;
1334 }
1335
1336 if (!p)
1337 {
1338 /* try to bring sname into play (it may be already) */
1339 if (mess->sname[0] == 0)
1340 overload[2] |= 2;
1341
1342 if (overload[2] & 2)
1343 {
1344 p = dhcp_skip_opts(mess->sname);
1345 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1346 p = NULL;
1347 }
1348 }
1349 }
1350
1351 if (!p)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001352 my_syslog(LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001353 }
1354
1355 if (p)
1356 {
1357 *(p++) = opt;
1358 *(p++) = len;
1359 }
1360
1361 return p;
1362}
1363
1364static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1365{
1366 int i;
1367 unsigned char *p = free_space(mess, end, opt, len);
1368
1369 if (p)
1370 for (i = 0; i < len; i++)
1371 *(p++) = val >> (8 * (len - (i + 1)));
1372}
1373
1374static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1375 char *string, int null_term)
1376{
1377 unsigned char *p;
1378 size_t len = strlen(string);
1379
1380 if (null_term && len != 255)
1381 len++;
1382
1383 if ((p = free_space(mess, end, opt, len)))
1384 memcpy(p, string, len);
1385}
1386
1387/* return length, note this only does the data part */
1388static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct in_addr local, int null_term)
1389{
1390 int len = opt->len;
1391
1392 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1393 len++;
1394
1395 if (p && len != 0)
1396 {
Simon Kelley6b010842007-02-12 20:32:07 +00001397 if ((opt->flags & DHOPT_ADDR) && !(opt->flags & DHOPT_ENCAPSULATE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001398 {
1399 int j;
1400 struct in_addr *a = (struct in_addr *)opt->val;
1401 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1402 {
1403 /* zero means "self" (but not in vendorclass options.) */
1404 if (a->s_addr == 0)
1405 memcpy(p, &local, INADDRSZ);
1406 else
1407 memcpy(p, a, INADDRSZ);
1408 p += INADDRSZ;
1409 }
1410 }
1411 else
1412 memcpy(p, opt->val, len);
1413 }
1414 return len;
1415}
1416
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001417static int in_list(unsigned char *list, int opt)
1418{
1419 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001420
1421 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001422 if (!list)
1423 return 1;
1424
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001425 for (i = 0; list[i] != OPTION_END; i++)
1426 if (opt == list[i])
1427 return 1;
1428
1429 return 0;
1430}
1431
Simon Kelleya2226412004-05-13 20:27:08 +01001432static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001433{
Simon Kelley91dccd02005-03-31 17:48:32 +01001434 struct dhcp_opt *tmp;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001435 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelley6b010842007-02-12 20:32:07 +00001436 if (tmp->opt == opt && !(tmp->flags & DHOPT_ENCAPSULATE))
Simon Kelleycdeda282006-03-16 20:16:06 +00001437 if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
1438 return tmp;
Simon Kelleya2226412004-05-13 20:27:08 +01001439
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001440 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001441}
1442
Simon Kelley6b010842007-02-12 20:32:07 +00001443/* mark vendor-encapsulated options which match the client-supplied or
1444 config-supplied vendor class */
1445static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1446{
1447 for (; dopt; dopt = dopt->next)
1448 {
1449 dopt->flags &= ~DHOPT_VENDOR_MATCH;
1450 if (opt && (dopt->flags & DHOPT_ENCAPSULATE))
1451 {
1452 int i, len = 0;
1453 if (dopt->vendor_class)
1454 len = strlen((char *)dopt->vendor_class);
1455 for (i = 0; i <= (option_len(opt) - len); i++)
1456 if (len == 0 || memcmp(dopt->vendor_class, option_ptr(opt)+i, len) == 0)
1457 {
1458 dopt->flags |= DHOPT_VENDOR_MATCH;
1459 break;
1460 }
1461 }
1462 }
1463}
1464
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001465static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
Simon Kelley91dccd02005-03-31 17:48:32 +01001466{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001467 memset(mess->sname, 0, sizeof(mess->sname));
1468 memset(mess->file, 0, sizeof(mess->file));
1469 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
1470 mess->siaddr.s_addr = 0;
1471}
Simon Kelleycdeda282006-03-16 20:16:06 +00001472
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001473static void do_options(struct dhcp_context *context,
1474 struct dhcp_packet *mess,
1475 unsigned char *real_end,
1476 unsigned char *req_options,
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001477 char *hostname,
1478 struct dhcp_netid *netid,
1479 struct in_addr subnet_addr,
1480 unsigned char fqdn_flags,
1481 int null_term,
1482 unsigned char *agent_id)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001483{
Simon Kelley3be34542004-09-11 19:12:13 +01001484 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001485 struct dhcp_boot *boot;
1486 unsigned char *p, *end = agent_id ? agent_id : real_end;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001487 int i, len, force_encap = 0;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001488 unsigned char f0 = 0, s0 = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001489 int done_file = 0, done_server = 0;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001490
Simon Kelleyf2621c72007-04-29 19:47:21 +01001491 /* logging */
1492 if ((daemon->options & OPT_LOG_OPTS) && req_options)
1493 {
1494 char *q = daemon->namebuff;
1495 for (i = 0; req_options[i] != OPTION_END; i++)
1496 {
1497 char *s = option_string(req_options[i]);
Simon Kelley824af852008-02-12 20:43:05 +00001498 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1499 "%d%s%s%s",
1500 req_options[i],
1501 s ? ":" : "",
1502 s ? s : "",
1503 req_options[i+1] == OPTION_END ? "" : ", ");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001504 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
1505 {
1506 q = daemon->namebuff;
1507 my_syslog(LOG_INFO, _("requested options: %s"), daemon->namebuff);
1508 }
1509 }
1510 }
1511
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001512 /* decide which dhcp-boot option we're using */
1513 for (boot = daemon->boot_config; boot; boot = boot->next)
1514 if (match_netid(boot->netid, netid, 0))
1515 break;
1516 if (!boot)
1517 /* No match, look for one without a netid */
1518 for (boot = daemon->boot_config; boot; boot = boot->next)
1519 if (match_netid(boot->netid, netid, 1))
1520 break;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001521
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001522 mess->siaddr = context->local;
1523
1524 /* See if we can send the boot stuff as options.
1525 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00001526 and very old DHCP clients won't have this, we also
1527 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001528 Some PXE ROMs have bugs (surprise!) and need zero-terminated
1529 names, so we always send those. */
1530 if (boot)
1531 {
1532 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00001533 {
1534 if (!(daemon->options & OPT_NO_OVERRIDE) &&
1535 req_options &&
1536 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001537 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
1538 else
Simon Kelley824af852008-02-12 20:43:05 +00001539 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001540 }
1541
1542 if (boot->file)
1543 {
Simon Kelley824af852008-02-12 20:43:05 +00001544 if (!(daemon->options & OPT_NO_OVERRIDE) &&
1545 req_options &&
1546 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001547 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
1548 else
Simon Kelley824af852008-02-12 20:43:05 +00001549 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001550 }
1551
1552 if (boot->next_server.s_addr)
1553 mess->siaddr = boot->next_server;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001554
1555 if (daemon->options & OPT_LOG_OPTS)
1556 my_syslog(LOG_INFO, _("next server: %s"), inet_ntoa(mess->siaddr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001557 }
Simon Kelley824af852008-02-12 20:43:05 +00001558 else
1559 /* Use the values of the relevant options if no dhcp-boot given and
1560 they're no explicitly asked for as options. */
1561 {
1562 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
1563 (opt = option_find2(netid, config_opts, OPTION_FILENAME)))
1564 {
1565 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
1566 done_file = 1;
1567 }
1568
1569 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
1570 (opt = option_find2(netid, config_opts, OPTION_SNAME)))
1571 {
1572 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
1573 done_server = 1;
1574 }
1575 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001576
Simon Kelley824af852008-02-12 20:43:05 +00001577 if (daemon->options & OPT_LOG_OPTS)
1578 {
1579 if (strlen((char *)mess->file) != 0)
1580 my_syslog(LOG_INFO, _("bootfile name: %s"), (char *)mess->file);
1581
1582 if (strlen((char *)mess->sname) != 0)
1583 my_syslog(LOG_INFO, _("server name: %s"), (char *)mess->sname);
1584 }
1585
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001586 /* We don't want to do option-overload for BOOTP, so make the file and sname
1587 fields look like they are in use, even when they aren't. This gets restored
1588 at the end of this function. */
1589
Simon Kelley824af852008-02-12 20:43:05 +00001590 if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001591 {
1592 f0 = mess->file[0];
1593 mess->file[0] = 1;
1594 s0 = mess->sname[0];
1595 mess->sname[0] = 1;
1596 }
1597
1598 /* At this point, if mess->sname or mess->file are zeroed, they are available
1599 for option overload, reserve space for the overload option. */
1600 if (mess->file[0] == 0 || mess->sname[0] == 0)
1601 end -= 3;
1602
Simon Kelley3be34542004-09-11 19:12:13 +01001603 /* rfc3011 says this doesn't need to be in the requested options list. */
1604 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001605 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley3be34542004-09-11 19:12:13 +01001606
Simon Kelley832af0b2007-01-21 20:01:28 +00001607 if (!option_find2(netid, config_opts, OPTION_NETMASK))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001608 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001609
Simon Kelley3be34542004-09-11 19:12:13 +01001610 /* May not have a "guessed" broadcast address if we got no packets via a relay
1611 from this net yet (ie just unicast renewals after a restart */
1612 if (context->broadcast.s_addr &&
Simon Kelley33820b72004-04-03 21:10:00 +01001613 !option_find2(netid, config_opts, OPTION_BROADCAST))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001614 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001615
Simon Kelley3be34542004-09-11 19:12:13 +01001616 /* Same comments as broadcast apply, and also may not be able to get a sensible
1617 default when using subnet select. User must configure by steam in that case. */
1618 if (context->router.s_addr &&
1619 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001620 !option_find2(netid, config_opts, OPTION_ROUTER))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001621 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001622
1623 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001624 !option_find2(netid, config_opts, OPTION_DNSSERVER))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001625 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001626
Simon Kelley3be34542004-09-11 19:12:13 +01001627 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001628 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001629 option_put_string(mess, end, OPTION_DOMAINNAME, daemon->domain_suffix, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001630
Simon Kelley824af852008-02-12 20:43:05 +00001631 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01001632 if (hostname)
1633 {
Simon Kelley824af852008-02-12 20:43:05 +00001634 if (in_list(req_options, OPTION_HOSTNAME) &&
1635 !option_find2(netid, config_opts, OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001636 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01001637
1638 if (fqdn_flags != 0)
1639 {
1640 int len = strlen(hostname) + 3;
1641 if (fqdn_flags & 0x04)
1642 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00001643 else if (null_term)
1644 len++;
1645
Simon Kelley3d8df262005-08-29 12:19:27 +01001646 if (daemon->domain_suffix)
1647 len += strlen(daemon->domain_suffix) + 1;
1648
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001649 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01001650 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001651 *(p++) = fqdn_flags;
1652 *(p++) = 255;
1653 *(p++) = 255;
1654
1655 if (fqdn_flags & 0x04)
1656 {
1657 p = do_rfc1035_name(p, hostname);
1658 if (daemon->domain_suffix)
1659 p = do_rfc1035_name(p, daemon->domain_suffix);
1660 *p++ = 0;
1661 }
1662 else
1663 {
1664 memcpy(p, hostname, strlen(hostname));
1665 p += strlen(hostname);
1666 if (daemon->domain_suffix)
1667 {
1668 *(p++) = '.';
1669 memcpy(p, daemon->domain_suffix, strlen(daemon->domain_suffix));
1670 p += strlen(daemon->domain_suffix);
Simon Kelleycdeda282006-03-16 20:16:06 +00001671 }
1672 if (null_term)
1673 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01001674 }
1675 }
1676 }
1677 }
1678
Simon Kelley6b010842007-02-12 20:32:07 +00001679 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001680 {
Simon Kelley824af852008-02-12 20:43:05 +00001681 int optno = opt->opt;
1682
Simon Kelley6b010842007-02-12 20:32:07 +00001683 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00001684 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00001685 continue;
1686
1687 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00001688 if (optno == OPTION_CLIENT_FQDN ||
1689 optno == OPTION_MAXMESSAGE ||
1690 optno == OPTION_OVERLOAD ||
1691 optno == OPTION_PAD ||
1692 optno == OPTION_END)
1693 continue;
1694
1695 if (optno == OPTION_SNAME && done_server)
1696 continue;
1697
1698 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00001699 continue;
1700
1701 /* netids match and not encapsulated? */
Simon Kelley824af852008-02-12 20:43:05 +00001702 if (opt != option_find2(netid, config_opts, optno))
Simon Kelley33820b72004-04-03 21:10:00 +01001703 continue;
1704
1705 /* For the options we have default values on
1706 dhc-option=<optionno> means "don't include this option"
1707 not "include a zero-length option" */
1708 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00001709 (optno == OPTION_NETMASK ||
1710 optno == OPTION_BROADCAST ||
1711 optno == OPTION_ROUTER ||
1712 optno == OPTION_DNSSERVER ||
1713 optno == OPTION_DOMAINNAME ||
1714 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01001715 continue;
Simon Kelley824af852008-02-12 20:43:05 +00001716
1717 /* always force null-term for filename ans servername - buggy PXE again. */
1718 len = do_opt(opt, NULL, context->local,
1719 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001720
Simon Kelley824af852008-02-12 20:43:05 +00001721 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00001722 {
Simon Kelley824af852008-02-12 20:43:05 +00001723 do_opt(opt, p, context->local,
1724 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
1725
Simon Kelley6b010842007-02-12 20:32:07 +00001726 /* If we send a vendor-id, revisit which vendor-ops we consider
1727 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00001728 if (optno == OPTION_VENDOR_ID)
Simon Kelley6b010842007-02-12 20:32:07 +00001729 match_vendor_opts(p - 2, config_opts);
1730 }
1731 }
1732
1733 /* prune encapsulated options based on netid, and look if we're forcing them to be sent */
1734 for (opt = config_opts; opt; opt = opt->next)
1735 if (opt->flags & DHOPT_VENDOR_MATCH)
1736 {
1737 if (!match_netid(opt->netid, netid, 1) && !match_netid(opt->netid, netid, 0))
1738 opt->flags &= ~DHOPT_VENDOR_MATCH;
1739 else if (opt->flags & DHOPT_FORCE)
1740 force_encap = 1;
1741 }
1742
1743 if (force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001744 {
1745 int enc_len = 0;
Simon Kelley6b010842007-02-12 20:32:07 +00001746 struct dhcp_opt *start;
1747
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001748 /* find size in advance */
Simon Kelley6b010842007-02-12 20:32:07 +00001749 for (start = opt = config_opts; opt; opt = opt->next)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001750 if (opt->flags & DHOPT_VENDOR_MATCH)
Simon Kelley91dccd02005-03-31 17:48:32 +01001751 {
Simon Kelley6b010842007-02-12 20:32:07 +00001752 int new = do_opt(opt, NULL, context->local, null_term) + 2;
1753 if (enc_len + new <= 255)
1754 enc_len += new;
Simon Kelley91dccd02005-03-31 17:48:32 +01001755 else
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001756 {
Simon Kelley6b010842007-02-12 20:32:07 +00001757 p = free_space(mess, end, OPTION_VENDOR_CLASS_OPT, enc_len);
1758 for (; start && start != opt; start = start->next)
1759 if (p && (start->flags & DHOPT_VENDOR_MATCH))
1760 {
1761 len = do_opt(start, p + 2, context->local, null_term);
1762 *(p++) = start->opt;
1763 *(p++) = len;
1764 p += len;
1765 }
1766 enc_len = new;
1767 start = opt;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001768 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001769 }
Simon Kelley6b010842007-02-12 20:32:07 +00001770
1771 if (enc_len != 0 &&
1772 (p = free_space(mess, end, OPTION_VENDOR_CLASS_OPT, enc_len + 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001773 {
Simon Kelley6b010842007-02-12 20:32:07 +00001774 for (; start; start = start->next)
1775 if (start->flags & DHOPT_VENDOR_MATCH)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001776 {
Simon Kelley6b010842007-02-12 20:32:07 +00001777 len = do_opt(start, p + 2, context->local, null_term);
1778 *(p++) = start->opt;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001779 *(p++) = len;
Simon Kelley6b010842007-02-12 20:32:07 +00001780 p += len;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001781 }
Simon Kelley6b010842007-02-12 20:32:07 +00001782 *p = OPTION_END;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001783 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001784 }
1785
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001786 /* move agent_id back down to the end of the packet */
1787 if (agent_id)
1788 {
1789 p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1790 memmove(p, agent_id, real_end - agent_id);
1791 p += real_end - agent_id;
1792 memset(p, 0, real_end - p); /* in case of overlap */
1793 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001794
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001795 /* restore BOOTP anti-overload hack */
Simon Kelley824af852008-02-12 20:43:05 +00001796 if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001797 {
1798 mess->file[0] = f0;
1799 mess->sname[0] = s0;
1800 }
1801}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001802