blob: c9320eef4f298023ee7b3013e340963effc15bdd [file] [log] [blame]
Simon Kelley73a08a22009-02-05 20:28:08 +00001/* dnsmasq is Copyright (c) 2000-2009 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
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 Kelley1a6bca82008-07-11 11:11:42 +010059#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010060
Simon Kelley9e4abcb2004-01-22 19:47:41 +000061#define DHCPDISCOVER 1
62#define DHCPOFFER 2
63#define DHCPREQUEST 3
64#define DHCPDECLINE 4
65#define DHCPACK 5
66#define DHCPNAK 6
67#define DHCPRELEASE 7
68#define DHCPINFORM 8
69
Simon Kelley0a852542005-03-23 20:28:59 +000070#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010071#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
Simon Kelley1a6bca82008-07-11 11:11:42 +010072#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
Simon Kelley0a852542005-03-23 20:28:59 +000073
Simon Kelleyf2621c72007-04-29 19:47:21 +010074static int sanitise(unsigned char *opt, char *buf);
Simon Kelley73a08a22009-02-05 20:28:08 +000075static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
Simon Kelley824af852008-02-12 20:43:05 +000076static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000077static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
78static void option_put_string(struct dhcp_packet *mess, unsigned char *end,
79 int opt, char *string, int null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080static struct in_addr option_addr(unsigned char *opt);
Simon Kelley44a2a312004-03-10 20:04:35 +000081static unsigned int option_uint(unsigned char *opt, int size);
Simon Kelley5aabfc72007-08-29 11:24:47 +010082static void log_packet(char *type, void *addr,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000083 unsigned char *ext_mac, int mac_len, char *interface, char *string);
Simon Kelleycdeda282006-03-16 20:16:06 +000084static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010085static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
Simon Kelley5aabfc72007-08-29 11:24:47 +010086static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid);
Simon Kelley9e038942008-05-30 20:06:34 +010087static void clear_packet(struct dhcp_packet *mess, unsigned char *end, unsigned char *agent_id);
88static void restore_agent_id(unsigned char *agent_id, struct dhcp_packet *mess, unsigned char *real_end);
Simon Kelley1b7ecd12007-02-05 14:57:57 +000089static void do_options(struct dhcp_context *context,
90 struct dhcp_packet *mess,
91 unsigned char *real_end,
92 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +000093 char *hostname,
94 char *domain, char *config_domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +000095 struct dhcp_netid *netid,
96 struct in_addr subnet_addr,
97 unsigned char fqdn_flags,
98 int null_term,
99 unsigned char *agent_id);
Simon Kelley9009d742008-11-14 20:04:27 +0000100
Simon Kelley6b010842007-02-12 20:32:07 +0000101static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
Simon Kelley73a08a22009-02-05 20:28:08 +0000102static void do_encap_opts(int encap, struct dhcp_packet *mess, unsigned char *end, int null_term);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000103
Simon Kelley824af852008-02-12 20:43:05 +0000104size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
Simon Kelley5aabfc72007-08-29 11:24:47 +0100105 size_t sz, time_t now, int unicast_dest, int *is_inform)
Simon Kelley33820b72004-04-03 21:10:00 +0100106{
Simon Kelley26128d22004-11-14 16:43:54 +0000107 unsigned char *opt, *clid = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000108 struct dhcp_lease *ltmp, *lease = NULL;
Simon Kelleya2226412004-05-13 20:27:08 +0100109 struct dhcp_vendor *vendor;
Simon Kelleycdeda282006-03-16 20:16:06 +0000110 struct dhcp_mac *mac;
Simon Kelley26128d22004-11-14 16:43:54 +0000111 struct dhcp_netid_list *id_list;
Simon Kelley832af0b2007-01-21 20:01:28 +0000112 int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0;
Simon Kelley824af852008-02-12 20:43:05 +0000113 struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000114 unsigned char *end = (unsigned char *)(mess + 1);
Simon Kelley9009d742008-11-14 20:04:27 +0000115 char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000116 int hostname_auth = 0, borken_opt = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100117 unsigned char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +0000118 char *message = NULL;
Simon Kelley59353a62004-11-21 19:34:28 +0000119 unsigned int time;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000120 struct dhcp_config *config;
Simon Kelley9009d742008-11-14 20:04:27 +0000121 struct dhcp_netid *netid;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100122 struct in_addr subnet_addr, fallback, override;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100123 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100124 unsigned int mess_type = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100125 unsigned char fqdn_flags = 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100126 unsigned char *agent_id = NULL;
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000127 unsigned char *emac = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100128 int emac_len = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000129 struct dhcp_netid known_id, iface_id;
Simon Kelley73a08a22009-02-05 20:28:08 +0000130 struct dhcp_opt *o;
Simon Kelley3be34542004-09-11 19:12:13 +0100131
Simon Kelley1a6bca82008-07-11 11:11:42 +0100132 subnet_addr.s_addr = override.s_addr = 0;
Simon Kelley9009d742008-11-14 20:04:27 +0000133
134 /* set tag with name == interface */
135 iface_id.net = iface_name;
136 iface_id.next = NULL;
137 netid = &iface_id;
Simon Kelley849a8352006-06-09 21:02:31 +0100138
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100139 if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
Simon Kelley33820b72004-04-03 21:10:00 +0100140 return 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000141
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100142 if (mess->htype == 0 && mess->hlen != 0)
Simon Kelleycdeda282006-03-16 20:16:06 +0000143 return 0;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100144
Simon Kelley3be34542004-09-11 19:12:13 +0100145 /* check for DHCP rather than BOOTP */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000146 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000147 {
Simon Kelley3be34542004-09-11 19:12:13 +0100148 mess_type = option_uint(opt, 1);
Simon Kelley44a2a312004-03-10 20:04:35 +0000149
Simon Kelley3be34542004-09-11 19:12:13 +0100150 /* only insist on a cookie for DHCP. */
151 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
152 return 0;
153
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100154 /* two things to note here: expand_buf may move the packet,
155 so reassign mess from daemon->packet. Also, the size
156 sent includes the IP and UDP headers, hence the magic "-28" */
157 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
158 {
159 size_t size = (size_t)option_uint(opt, 2) - 28;
160
161 if (size > DHCP_PACKET_MAX)
162 size = DHCP_PACKET_MAX;
163 else if (size < sizeof(struct dhcp_packet))
164 size = sizeof(struct dhcp_packet);
165
166 if (expand_buf(&daemon->dhcp_packet, size))
167 {
Simon Kelley824af852008-02-12 20:43:05 +0000168 mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100169 end = ((unsigned char *)mess) + size;
170 }
171 }
172
Simon Kelley3be34542004-09-11 19:12:13 +0100173 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
174 it can affect the context-determination code. */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000175 if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
Simon Kelley3be34542004-09-11 19:12:13 +0100176 mess->ciaddr.s_addr = 0;
177
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100178 if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
179 {
180 /* Any agent-id needs to be copied back out, verbatim, as the last option
181 in the packet. Here, we shift it to the very end of the buffer, if it doesn't
182 get overwritten, then it will be shuffled back at the end of processing.
183 Note that the incoming options must not be overwritten here, so there has to
184 be enough free space at the end of the packet to copy the option. */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100185 unsigned char *sopt;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100186 unsigned int total = option_len(opt) + 2;
187 unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
188 if (last_opt && last_opt < end - total)
189 {
190 agent_id = end - total;
191 memcpy(agent_id, opt, total);
192 }
193
194 /* look for RFC3527 Link selection sub-option */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100195 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 +0100196 subnet_addr = option_addr(sopt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100197
198 /* look for RFC5107 server-identifier-override */
199 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
200 override = option_addr(sopt);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100201
202 /* if a circuit-id or remote-is option is provided, exact-match to options. */
203 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
204 {
205 int search;
206
207 if (vendor->match_type == MATCH_CIRCUIT)
208 search = SUBOPT_CIRCUIT_ID;
209 else if (vendor->match_type == MATCH_REMOTE)
210 search = SUBOPT_REMOTE_ID;
211 else if (vendor->match_type == MATCH_SUBSCRIBER)
212 search = SUBOPT_SUBSCR_ID;
213 else
214 continue;
215
Simon Kelley1a6bca82008-07-11 11:11:42 +0100216 if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
Simon Kelleyf2621c72007-04-29 19:47:21 +0100217 vendor->len == option_len(sopt) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100218 memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100219 {
220 vendor->netid.next = netid;
221 netid = &vendor->netid;
222 break;
223 }
224 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100225 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100226
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100227 /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
228 if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
Simon Kelley3be34542004-09-11 19:12:13 +0100229 subnet_addr = option_addr(opt);
Simon Kelley26128d22004-11-14 16:43:54 +0000230
231 /* If there is no client identifier option, use the hardware address */
Simon Kelleybb01cb92004-12-13 20:56:23 +0000232 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
Simon Kelley26128d22004-11-14 16:43:54 +0000233 {
Simon Kelley26128d22004-11-14 16:43:54 +0000234 clid_len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100235 clid = option_ptr(opt, 0);
Simon Kelley26128d22004-11-14 16:43:54 +0000236 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000237
Simon Kelley0a852542005-03-23 20:28:59 +0000238 /* do we have a lease in store? */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100239 lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
Simon Kelley0a852542005-03-23 20:28:59 +0000240
241 /* If this request is missing a clid, but we've seen one before,
242 use it again for option matching etc. */
243 if (lease && !clid && lease->clid)
244 {
245 clid_len = lease->clid_len;
246 clid = lease->clid;
247 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000248
249 /* find mac to use for logging and hashing */
250 emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
Simon Kelley44a2a312004-03-10 20:04:35 +0000251 }
Simon Kelley3be34542004-09-11 19:12:13 +0100252
Simon Kelleycdeda282006-03-16 20:16:06 +0000253 for (mac = daemon->dhcp_macs; mac; mac = mac->next)
254 if (mac->hwaddr_len == mess->hlen &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100255 (mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
256 memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
Simon Kelleycdeda282006-03-16 20:16:06 +0000257 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100258 mac->netid.next = netid;
259 netid = &mac->netid;
Simon Kelleycdeda282006-03-16 20:16:06 +0000260 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100261
Simon Kelley0a852542005-03-23 20:28:59 +0000262 /* Determine network for this packet. Our caller will have already linked all the
263 contexts which match the addresses of the receiving interface but if the
264 machine has an address already, or came via a relay, or we have a subnet selector,
265 we search again. If we don't have have a giaddr or explicit subnet selector,
266 use the ciaddr. This is necessary because a machine which got a lease via a
Simon Kelley3d8df262005-08-29 12:19:27 +0100267 relay won't use the relay to renew. If matching a ciaddr fails but we have a context
268 from the physical network, continue using that to allow correct DHCPNAK generation later. */
Simon Kelley0a852542005-03-23 20:28:59 +0000269 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
270 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100271 struct dhcp_context *context_tmp, *context_new = NULL;
Simon Kelley16972692006-10-16 20:04:18 +0100272 struct in_addr addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100273 int force = 0;
Simon Kelley0a852542005-03-23 20:28:59 +0000274
Simon Kelley3d8df262005-08-29 12:19:27 +0100275 if (subnet_addr.s_addr)
276 {
277 addr = subnet_addr;
278 force = 1;
279 }
280 else if (mess->giaddr.s_addr)
281 {
282 addr = mess->giaddr;
283 force = 1;
284 }
Simon Kelley16972692006-10-16 20:04:18 +0100285 else
286 {
287 /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
288 addr = mess->ciaddr;
289 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
290 if (context_tmp->netmask.s_addr &&
291 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
292 is_same_net(addr, context_tmp->end, context_tmp->netmask))
293 {
294 context_new = context;
295 break;
296 }
297 }
298
299 if (!context_new)
300 for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
301 if (context_tmp->netmask.s_addr &&
302 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
303 is_same_net(addr, context_tmp->end, context_tmp->netmask))
304 {
305 context_tmp->current = context_new;
306 context_new = context_tmp;
307 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100308
Simon Kelley3d8df262005-08-29 12:19:27 +0100309 if (context_new || force)
310 context = context_new;
Simon Kelley16972692006-10-16 20:04:18 +0100311
Simon Kelley0a852542005-03-23 20:28:59 +0000312 }
Simon Kelley3be34542004-09-11 19:12:13 +0100313
314 if (!context)
315 {
Simon Kelleyf2621c72007-04-29 19:47:21 +0100316 my_syslog(LOG_WARNING, _("no address range available for DHCP request %s %s"),
317 subnet_addr.s_addr ? _("with subnet selector") : _("via"),
318 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 +0100319 return 0;
320 }
Simon Kelleyf2621c72007-04-29 19:47:21 +0100321
Simon Kelleyaedef832006-01-22 14:02:31 +0000322 /* keep _a_ local address available. */
323 fallback = context->local;
324
Simon Kelleyf2621c72007-04-29 19:47:21 +0100325 if (daemon->options & OPT_LOG_OPTS)
326 {
327 struct dhcp_context *context_tmp;
328 my_syslog(LOG_INFO, _("DHCP packet: transaction-id is %u"), mess->xid);
329 for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
330 {
331 strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
332 if (context_tmp->flags & CONTEXT_STATIC)
333 my_syslog(LOG_INFO, _("Available DHCP subnet: %s/%s"), daemon->namebuff, inet_ntoa(context_tmp->netmask));
334 else
335 my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
336 }
337 }
338
Simon Kelley3be34542004-09-11 19:12:13 +0100339 mess->op = BOOTREPLY;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100340
Simon Kelleycdeda282006-03-16 20:16:06 +0000341 config = find_config(daemon->dhcp_conf, context, clid, clid_len,
342 mess->chaddr, mess->hlen, mess->htype, NULL);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100343
344 /* set "known" tag for known hosts */
345 if (config)
346 {
347 known_id.net = "known";
348 known_id.next = netid;
349 netid = &known_id;
350 }
Simon Kelley26128d22004-11-14 16:43:54 +0000351
Simon Kelley3be34542004-09-11 19:12:13 +0100352 if (mess_type == 0)
353 {
354 /* BOOTP request */
Simon Kelley6b010842007-02-12 20:32:07 +0000355 struct dhcp_netid id, bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000356 struct in_addr *logaddr = NULL;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100357
358 /* must have a MAC addr for bootp */
359 if (mess->htype == 0 || mess->hlen == 0)
360 return 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000361
Simon Kelley26128d22004-11-14 16:43:54 +0000362 if (have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000363 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000364
365 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
366
367 if (have_config(config, CONFIG_NAME))
Simon Kelley9009d742008-11-14 20:04:27 +0000368 {
369 hostname = config->hostname;
370 domain = config->domain;
371 }
372
Simon Kelley26128d22004-11-14 16:43:54 +0000373 if (have_config(config, CONFIG_NETID))
374 {
375 config->netid.next = netid;
376 netid = &config->netid;
377 }
378
379 /* Match incoming filename field as a netid. */
380 if (mess->file[0])
381 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000382 memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
383 daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
384 id.net = (char *)daemon->dhcp_buff2;
Simon Kelley26128d22004-11-14 16:43:54 +0000385 id.next = netid;
386 netid = &id;
387 }
Simon Kelley6b010842007-02-12 20:32:07 +0000388
389 /* Add "bootp" as a tag to allow different options, address ranges etc
390 for BOOTP clients */
391 bootp_id.net = "bootp";
392 bootp_id.next = netid;
393 netid = &bootp_id;
Simon Kelley26128d22004-11-14 16:43:54 +0000394
395 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000396 if (match_netid(id_list->list, netid, 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000397 message = _("disabled");
Simon Kelley26128d22004-11-14 16:43:54 +0000398
Simon Kelley3d8df262005-08-29 12:19:27 +0100399 if (!message)
400 {
Simon Kelley9009d742008-11-14 20:04:27 +0000401 int nailed = 0;
402
Simon Kelley3d8df262005-08-29 12:19:27 +0100403 if (have_config(config, CONFIG_ADDR))
404 {
Simon Kelley9009d742008-11-14 20:04:27 +0000405 nailed = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100406 logaddr = &config->addr;
407 mess->yiaddr = config->addr;
408 if ((lease = lease_find_by_addr(config->addr)) &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000409 (lease->hwaddr_len != mess->hlen ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100410 lease->hwaddr_type != mess->htype ||
Simon Kelleycdeda282006-03-16 20:16:06 +0000411 memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000412 message = _("address in use");
Simon Kelley3d8df262005-08-29 12:19:27 +0100413 }
Simon Kelley3d8df262005-08-29 12:19:27 +0100414 else
415 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000416 if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
Simon Kelley824af852008-02-12 20:43:05 +0000417 !address_available(context, lease->addr, netid))
Simon Kelleye17fb622006-01-14 20:33:46 +0000418 {
419 if (lease)
420 {
421 /* lease exists, wrong network. */
422 lease_prune(lease, now);
423 lease = NULL;
424 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100425 if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
Simon Kelleye17fb622006-01-14 20:33:46 +0000426 message = _("no address available");
427 }
428 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100429 mess->yiaddr = lease->addr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100430 }
431
Simon Kelley9009d742008-11-14 20:04:27 +0000432 if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
433 message = _("wrong network");
434 else if (context->netid.net)
435 {
436 context->netid.next = netid;
437 netid = &context->netid;
438 }
439
440 if (!message && !nailed)
441 {
442 for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
443 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
444 break;
445 if (!id_list)
446 message = _("no address configured");
447 }
448
Simon Kelley7cebd202006-05-06 14:13:33 +0100449 if (!message &&
450 !lease &&
451 (!(lease = lease_allocate(mess->yiaddr))))
Simon Kelley824af852008-02-12 20:43:05 +0000452 message = _("no leases left");
Simon Kelley9009d742008-11-14 20:04:27 +0000453
Simon Kelley3d8df262005-08-29 12:19:27 +0100454 if (!message)
455 {
456 logaddr = &mess->yiaddr;
Simon Kelley3d8df262005-08-29 12:19:27 +0100457
Simon Kelleycdeda282006-03-16 20:16:06 +0000458 lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100459 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +0000460 lease_set_hostname(lease, hostname, 1);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100461 /* infinite lease unless nailed in dhcp-host line. */
462 lease_set_expires(lease,
463 have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff,
464 now);
Simon Kelley824af852008-02-12 20:43:05 +0000465 lease_set_interface(lease, int_index);
Simon Kelley3d8df262005-08-29 12:19:27 +0100466
Simon Kelley9e038942008-05-30 20:06:34 +0100467 clear_packet(mess, end, NULL);
Simon Kelley9009d742008-11-14 20:04:27 +0000468 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
469 domain, netid, subnet_addr, 0, 0, NULL);
Simon Kelley3d8df262005-08-29 12:19:27 +0100470 }
471 }
472
Simon Kelley5aabfc72007-08-29 11:24:47 +0100473 log_packet(NULL, logaddr, mess->chaddr, mess->hlen, iface_name, message);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000474
Simon Kelley5aabfc72007-08-29 11:24:47 +0100475 return message ? 0 : dhcp_packet_size(mess, netid);
Simon Kelley3be34542004-09-11 19:12:13 +0100476 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000477
Simon Kelley3d8df262005-08-29 12:19:27 +0100478 if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
479 {
480 /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
481 int len = option_len(opt);
482 char *pq = daemon->dhcp_buff;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100483 unsigned char *pp, *op = option_ptr(opt, 0);
Simon Kelley3d8df262005-08-29 12:19:27 +0100484
485 fqdn_flags = *op;
486 len -= 3;
487 op += 3;
488 pp = op;
489
490 /* Always force update, since the client has no way to do it itself. */
Simon Kelleycdeda282006-03-16 20:16:06 +0000491 if (!(fqdn_flags & 0x01))
Simon Kelley3d8df262005-08-29 12:19:27 +0100492 fqdn_flags |= 0x02;
493
494 fqdn_flags &= ~0x08;
495 fqdn_flags |= 0x01;
496
497 if (fqdn_flags & 0x04)
498 while (*op != 0 && ((op + (*op) + 1) - pp) < len)
499 {
500 memcpy(pq, op+1, *op);
501 pq += *op;
502 op += (*op)+1;
503 *(pq++) = '.';
504 }
505 else
506 {
507 memcpy(pq, op, len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000508 if (len > 0 && op[len-1] == 0)
509 borken_opt = 1;
Simon Kelley3d8df262005-08-29 12:19:27 +0100510 pq += len + 1;
511 }
512
513 if (pq != daemon->dhcp_buff)
514 pq--;
515
516 *pq = 0;
517
518 if (canonicalise(daemon->dhcp_buff))
519 offer_hostname = client_hostname = daemon->dhcp_buff;
520 }
Simon Kelleybb01cb92004-12-13 20:56:23 +0000521 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000522 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000523 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100524 memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
Simon Kelleycdeda282006-03-16 20:16:06 +0000525 /* Microsoft clients are broken, and need zero-terminated strings
526 in options. We detect this state here, and do the same in
527 any options we send */
528 if (len > 0 && daemon->dhcp_buff[len-1] == 0)
529 borken_opt = 1;
530 else
531 daemon->dhcp_buff[len] = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +0100532 if (canonicalise(daemon->dhcp_buff))
533 client_hostname = daemon->dhcp_buff;
534 }
535
536 if (have_config(config, CONFIG_NAME))
537 {
538 hostname = config->hostname;
Simon Kelley9009d742008-11-14 20:04:27 +0000539 domain = config->domain;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000540 hostname_auth = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000541 /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100542 if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
Simon Kelley832af0b2007-01-21 20:01:28 +0000543 offer_hostname = hostname;
Simon Kelley3d8df262005-08-29 12:19:27 +0100544 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100545 else if (client_hostname)
Simon Kelley3d8df262005-08-29 12:19:27 +0100546 {
Simon Kelley9009d742008-11-14 20:04:27 +0000547 domain = strip_hostname(client_hostname);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100548
549 if (strlen(client_hostname) != 0)
550 {
551 hostname = client_hostname;
552 if (!config)
553 {
554 /* Search again now we have a hostname.
555 Only accept configs without CLID and HWADDR here, (they won't match)
556 to avoid impersonation by name. */
557 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
558 mess->chaddr, mess->hlen,
559 mess->htype, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +0000560 if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
Simon Kelley824af852008-02-12 20:43:05 +0000561 {
562 config = new;
563 /* set "known" tag for known hosts */
564 known_id.net = "known";
565 known_id.next = netid;
566 netid = &known_id;
567 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100568 }
569 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000570 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100571
Simon Kelleya2226412004-05-13 20:27:08 +0100572 if (have_config(config, CONFIG_NETID))
573 {
574 config->netid.next = netid;
575 netid = &config->netid;
576 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100577
Simon Kelley73a08a22009-02-05 20:28:08 +0000578 /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
579 Otherwise assume the option is an array, and look for a matching element.
580 If no data given, existance of the option is enough. */
581 for (o = daemon->dhcp_match; o; o = o->next)
582 {
583 int i, matched = 0;
584
585 if (!(opt = option_find(mess, sz, o->opt, 1)) ||
586 o->len > option_len(opt))
587 continue;
588
589 if (o->len == 0)
590 matched = 1;
591 else if (o->flags & DHOPT_HEX)
592 {
593 if (memcmp_masked(o->val, option_ptr(opt, 0), o->len, o->u.wildcard_mask))
594 matched = 1;
595 }
596 else
597 for (i = 0; i <= (option_len(opt) - o->len); )
598 {
599 if (memcmp(o->val, option_ptr(opt, i), o->len) == 0)
600 {
601 matched = 1;
602 break;
603 }
604
605 if (o->flags & DHOPT_STRING)
606 i++;
607 else
608 i += o->len;
609 }
610
611 if (matched)
612 {
613 o->netid->next = netid;
614 netid = o->netid;
615 }
616 }
617
Simon Kelley26128d22004-11-14 16:43:54 +0000618 /* user-class options are, according to RFC3004, supposed to contain
619 a set of counted strings. Here we check that this is so (by seeing
620 if the counts are consistent with the overall option length) and if
621 so zero the counts so that we don't get spurious matches between
622 the vendor string and the counts. If the lengths don't add up, we
623 assume that the option is a single string and non RFC3004 compliant
Simon Kelley16972692006-10-16 20:04:18 +0100624 and just do the substring match. dhclient provides these broken options.
625 The code, later, which sends user-class data to the lease-change script
626 relies on the transformation done here.
627 */
Simon Kelleya2226412004-05-13 20:27:08 +0100628
Simon Kelleybb01cb92004-12-13 20:56:23 +0000629 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
Simon Kelleya2226412004-05-13 20:27:08 +0100630 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100631 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000632 int tmp, j;
Simon Kelley26128d22004-11-14 16:43:54 +0000633 for (j = 0; j < option_len(opt); j += ucp[j] + 1);
634 if (j == option_len(opt))
635 for (j = 0; j < option_len(opt); j = tmp)
636 {
637 tmp = j + ucp[j] + 1;
638 ucp[j] = 0;
639 }
Simon Kelleya2226412004-05-13 20:27:08 +0100640 }
Simon Kelley73a08a22009-02-05 20:28:08 +0000641
Simon Kelley26128d22004-11-14 16:43:54 +0000642 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100643 {
644 int mopt;
645
646 if (vendor->match_type == MATCH_VENDOR)
647 mopt = OPTION_VENDOR_ID;
648 else if (vendor->match_type == MATCH_USER)
649 mopt = OPTION_USER_CLASS;
650 else
651 continue;
652
653 if ((opt = option_find(mess, sz, mopt, 1)))
654 {
655 int i;
656 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100657 if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100658 {
659 vendor->netid.next = netid;
660 netid = &vendor->netid;
661 break;
662 }
663 }
664 }
665
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000666 /* mark vendor-encapsulated options which match the client-supplied vendor class */
Simon Kelley6b010842007-02-12 20:32:07 +0000667 match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000668
Simon Kelleyf2621c72007-04-29 19:47:21 +0100669 if (daemon->options & OPT_LOG_OPTS)
670 {
671 if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
672 my_syslog(LOG_INFO, _("Vendor class: %s"), daemon->namebuff);
673 if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
674 my_syslog(LOG_INFO, _("User class: %s"), daemon->namebuff);
675 }
676
Simon Kelley26128d22004-11-14 16:43:54 +0000677 /* if all the netids in the ignore list are present, ignore this client */
678 for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
Simon Kelleycdeda282006-03-16 20:16:06 +0000679 if (match_netid(id_list->list, netid, 0))
Simon Kelley26128d22004-11-14 16:43:54 +0000680 ignore = 1;
Simon Kelley832af0b2007-01-21 20:01:28 +0000681
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100682 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
683 if (have_config(config, CONFIG_NOCLID))
Simon Kelley0a852542005-03-23 20:28:59 +0000684 clid = NULL;
685
Simon Kelleybb01cb92004-12-13 20:56:23 +0000686 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100687 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100688 req_options = (unsigned char *)daemon->dhcp_buff2;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100689 memcpy(req_options, option_ptr(opt, 0), option_len(opt));
Simon Kelleybb01cb92004-12-13 20:56:23 +0000690 req_options[option_len(opt)] = OPTION_END;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100691 }
692
Simon Kelley3be34542004-09-11 19:12:13 +0100693 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000694 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000695 case DHCPDECLINE:
Simon Kelleybb01cb92004-12-13 20:56:23 +0000696 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000697 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000698 return 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100699
Simon Kelley44a2a312004-03-10 20:04:35 +0000700 /* sanitise any message. Paranoid? Moi? */
Simon Kelleyf2621c72007-04-29 19:47:21 +0100701 sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000702
Simon Kelleybb01cb92004-12-13 20:56:23 +0000703 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000704 return 0;
705
Simon Kelley1a6bca82008-07-11 11:11:42 +0100706 log_packet("DECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff);
Simon Kelley44a2a312004-03-10 20:04:35 +0000707
708 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
709 lease_prune(lease, now);
710
Simon Kelley33820b72004-04-03 21:10:00 +0100711 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000712 config->addr.s_addr == option_addr(opt).s_addr)
713 {
Simon Kelley849a8352006-06-09 21:02:31 +0100714 prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100715 my_syslog(LOG_WARNING, _("disabling DHCP static address %s for %s"),
716 inet_ntoa(config->addr), daemon->dhcp_buff);
Simon Kelley849a8352006-06-09 21:02:31 +0100717 config->flags |= CONFIG_DECLINED;
718 config->decline_time = now;
Simon Kelley44a2a312004-03-10 20:04:35 +0000719 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100720 else
721 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100722 for (; context; context = context->current)
723 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000724
725 return 0;
726
727 case DHCPRELEASE:
Simon Kelley824af852008-02-12 20:43:05 +0000728 if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
Simon Kelley16972692006-10-16 20:04:18 +0100729 !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
Simon Kelley73a08a22009-02-05 20:28:08 +0000730 option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
Simon Kelley44a2a312004-03-10 20:04:35 +0000731 return 0;
732
Simon Kelley44a2a312004-03-10 20:04:35 +0000733 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
734 lease_prune(lease, now);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100735 else
Simon Kelleyb8187c82005-11-26 21:46:27 +0000736 message = _("unknown lease");
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100737
Simon Kelley5aabfc72007-08-29 11:24:47 +0100738 log_packet("RELEASE", &mess->ciaddr, emac, emac_len, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000739
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000740 return 0;
741
742 case DHCPDISCOVER:
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100743 if (ignore || have_config(config, CONFIG_DISABLE))
744 {
Simon Kelleyb8187c82005-11-26 21:46:27 +0000745 message = _("ignored");
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100746 opt = NULL;
747 }
748 else
749 {
750 struct in_addr addr, conf;
751
Simon Kelley1a6bca82008-07-11 11:11:42 +0100752 addr.s_addr = conf.s_addr = 0;
753
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100754 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
755 addr = option_addr(opt);
756
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100757 if (have_config(config, CONFIG_ADDR))
758 {
Simon Kelley849a8352006-06-09 21:02:31 +0100759 char *addrs = inet_ntoa(config->addr);
760
Simon Kelley9009d742008-11-14 20:04:27 +0000761 if ((ltmp = lease_find_by_addr(config->addr)) &&
762 ltmp != lease &&
763 !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000764 {
765 int len;
766 unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
767 ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100768 my_syslog(LOG_WARNING, _("not using configured address %s because it is leased to %s"),
Simon Kelley5aabfc72007-08-29 11:24:47 +0100769 addrs, print_mac(daemon->namebuff, mac, len));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000770 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100771 else
772 {
773 struct dhcp_context *tmp;
774 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100775 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100776 break;
777 if (tmp)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100778 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 +0100779 else if (have_config(config, CONFIG_DECLINED) &&
780 difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
Simon Kelleyf2621c72007-04-29 19:47:21 +0100781 my_syslog(LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100782 else
783 conf = config->addr;
784 }
785 }
786
787 if (conf.s_addr)
788 mess->yiaddr = conf;
Simon Kelley824af852008-02-12 20:43:05 +0000789 else if (lease && address_available(context, lease->addr, netid))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100790 mess->yiaddr = lease->addr;
Simon Kelley824af852008-02-12 20:43:05 +0000791 else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100792 !config_find_by_address(daemon->dhcp_conf, addr))
793 mess->yiaddr = addr;
Simon Kelley5aabfc72007-08-29 11:24:47 +0100794 else if (emac_len == 0)
795 message = _("no unique-id");
796 else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100797 message = _("no address available");
798 }
799
Simon Kelley1a6bca82008-07-11 11:11:42 +0100800 log_packet("DISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message);
Simon Kelley3d8df262005-08-29 12:19:27 +0100801
Simon Kelley824af852008-02-12 20:43:05 +0000802 if (message || !(context = narrow_context(context, mess->yiaddr, netid)))
Simon Kelley33820b72004-04-03 21:10:00 +0100803 return 0;
Simon Kelleye17fb622006-01-14 20:33:46 +0000804
Simon Kelley5aabfc72007-08-29 11:24:47 +0100805 log_packet("OFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000806
Simon Kelleycdeda282006-03-16 20:16:06 +0000807 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +0000808 {
809 context->netid.next = netid;
810 netid = &context->netid;
811 }
812
Simon Kelley824af852008-02-12 20:43:05 +0000813 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelley9e038942008-05-30 20:06:34 +0100814 clear_packet(mess, end, agent_id);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000815 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
Simon Kelley73a08a22009-02-05 20:28:08 +0000816 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000817 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100818 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
Simon Kelley59353a62004-11-21 19:34:28 +0000819 if (time != 0xffffffff)
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100820 {
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000821 option_put(mess, end, OPTION_T1, 4, (time/2));
822 option_put(mess, end, OPTION_T2, 4, (time*7)/8);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100823 }
Simon Kelley9009d742008-11-14 20:04:27 +0000824 do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
825 domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000826
Simon Kelley5aabfc72007-08-29 11:24:47 +0100827 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000828
829 case DHCPREQUEST:
Simon Kelley26128d22004-11-14 16:43:54 +0000830 if (ignore || have_config(config, CONFIG_DISABLE))
831 return 0;
Simon Kelleybb01cb92004-12-13 20:56:23 +0000832 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000833 {
834 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000835 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000836
Simon Kelley4011c4e2006-10-28 16:26:19 +0100837 /* send vendor and user class info for new or recreated lease */
838 do_classes = 1;
839
Simon Kelleybb01cb92004-12-13 20:56:23 +0000840 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000841 {
Simon Kelley3be34542004-09-11 19:12:13 +0100842 /* SELECTING */
Simon Kelley832af0b2007-01-21 20:01:28 +0000843 selecting = 1;
844
Simon Kelley1a6bca82008-07-11 11:11:42 +0100845 if (override.s_addr != 0)
846 {
847 if (option_addr(opt).s_addr != override.s_addr)
848 return 0;
849 }
Simon Kelley9009d742008-11-14 20:04:27 +0000850 else
Simon Kelley1a6bca82008-07-11 11:11:42 +0100851 {
852 for (; context; context = context->current)
853 if (context->local.s_addr == option_addr(opt).s_addr)
854 break;
855
856 if (!context)
Simon Kelley9009d742008-11-14 20:04:27 +0000857 {
858 /* In auth mode, a REQUEST sent to the wrong server
859 should be faulted, so that the client establishes
860 communication with us, otherwise, silently ignore. */
861 if (!(daemon->options & OPT_AUTHORITATIVE))
862 return 0;
863 message = _("wrong server-ID");
864 }
Simon Kelley1a6bca82008-07-11 11:11:42 +0100865 }
Simon Kelleye17fb622006-01-14 20:33:46 +0000866
Simon Kelley3be34542004-09-11 19:12:13 +0100867 /* If a lease exists for this host and another address, squash it. */
868 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
869 {
870 lease_prune(lease, now);
871 lease = NULL;
872 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000873 }
Simon Kelley3be34542004-09-11 19:12:13 +0100874 else
875 {
876 /* INIT-REBOOT */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100877 if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
Simon Kelley3be34542004-09-11 19:12:13 +0100878 return 0;
879
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100880 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
Simon Kelley824af852008-02-12 20:43:05 +0000881 {
882 message = _("wrong address");
883 /* avoid loops when client brain-dead */
884 lease_prune(lease, now);
885 lease = NULL;
886 }
Simon Kelley3be34542004-09-11 19:12:13 +0100887 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000888 }
889 else
890 {
891 /* RENEWING or REBINDING */
Simon Kelleycdeda282006-03-16 20:16:06 +0000892 /* Check existing lease for this address.
893 We allow it to be missing if dhcp-authoritative mode
894 as long as we can allocate the lease now - checked below.
895 This makes for a smooth recovery from a lost lease DB */
896 if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
897 (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000898 {
899 message = _("lease not found");
900 /* ensure we broadcast NAK */
901 unicast_dest = 0;
902 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100903 /* desynchronise renewals */
904 fuzz = rand16();
Simon Kelley3be34542004-09-11 19:12:13 +0100905 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000906 }
907
Simon Kelley5aabfc72007-08-29 11:24:47 +0100908 log_packet("REQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL);
Simon Kelleye17fb622006-01-14 20:33:46 +0000909
Simon Kelleydfa666f2004-08-02 18:27:27 +0100910 if (!message)
911 {
912 struct dhcp_config *addr_config;
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100913 struct dhcp_context *tmp = NULL;
914
915 if (have_config(config, CONFIG_ADDR))
916 for (tmp = context; tmp; tmp = tmp->current)
Simon Kelley849a8352006-06-09 21:02:31 +0100917 if (context->router.s_addr == config->addr.s_addr)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100918 break;
Simon Kelleyaedef832006-01-22 14:02:31 +0000919
Simon Kelley824af852008-02-12 20:43:05 +0000920 if (!(context = narrow_context(context, mess->yiaddr, netid)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000921 {
Simon Kelleye17fb622006-01-14 20:33:46 +0000922 /* If a machine moves networks whilst it has a lease, we catch that here. */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000923 message = _("wrong network");
924 /* ensure we broadcast NAK */
925 unicast_dest = 0;
926 }
927
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100928 /* Check for renewal of a lease which is outside the allowed range. */
Simon Kelley824af852008-02-12 20:43:05 +0000929 else if (!address_available(context, mess->yiaddr, netid) &&
Simon Kelleydfa666f2004-08-02 18:27:27 +0100930 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000931 message = _("address not available");
Simon Kelleye17fb622006-01-14 20:33:46 +0000932
Simon Kelleydfa666f2004-08-02 18:27:27 +0100933 /* Check if a new static address has been configured. Be very sure that
934 when the client does DISCOVER, it will get the static address, otherwise
935 an endless protocol loop will ensue. */
Simon Kelley832af0b2007-01-21 20:01:28 +0000936 else if (!tmp && !selecting &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100937 have_config(config, CONFIG_ADDR) &&
Simon Kelley849a8352006-06-09 21:02:31 +0100938 (!have_config(config, CONFIG_DECLINED) ||
939 difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100940 config->addr.s_addr != mess->yiaddr.s_addr &&
941 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000942 message = _("static lease available");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100943
944 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100945 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleyb8187c82005-11-26 21:46:27 +0000946 message = _("address reserved");
Simon Kelleydfa666f2004-08-02 18:27:27 +0100947
Simon Kelley9009d742008-11-14 20:04:27 +0000948 else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
949 {
950 /* If a host is configured with more than one MAC address, it's OK to 'nix
951 a lease from one of it's MACs to give the address to another. */
952 if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
953 {
954 my_syslog(LOG_INFO, _("abandoning lease to %s of %s"),
955 print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
956 inet_ntoa(ltmp->addr));
957 lease = ltmp;
958 }
Simon Kelley16972692006-10-16 20:04:18 +0100959 else
Simon Kelley9009d742008-11-14 20:04:27 +0000960 message = _("address in use");
961 }
962
963 if (!message)
964 {
965 if (emac_len == 0)
966 message = _("no unique-id");
967
968 else if (!lease)
969 {
970 if ((lease = lease_allocate(mess->yiaddr)))
971 do_classes = 1;
972 else
973 message = _("no leases left");
974 }
Simon Kelley16972692006-10-16 20:04:18 +0100975 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100976 }
Simon Kelley16972692006-10-16 20:04:18 +0100977
Simon Kelley44a2a312004-03-10 20:04:35 +0000978 if (message)
979 {
Simon Kelley5aabfc72007-08-29 11:24:47 +0100980 log_packet("NAK", &mess->yiaddr, emac, emac_len, iface_name, message);
Simon Kelley44a2a312004-03-10 20:04:35 +0000981
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000982 mess->yiaddr.s_addr = 0;
Simon Kelley9e038942008-05-30 20:06:34 +0100983 clear_packet(mess, end, agent_id);
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000984 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
Simon Kelley73a08a22009-02-05 20:28:08 +0000985 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000986 option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
Simon Kelley9e038942008-05-30 20:06:34 +0100987 /* DHCPNAK gets agent-id too */
988 restore_agent_id(agent_id, mess, end);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000989 /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on
990 a distant subnet which unicast a REQ to us won't work. */
991 if (!unicast_dest || mess->giaddr.s_addr != 0 ||
992 mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
993 {
994 mess->flags |= htons(0x8000); /* broadcast */
995 mess->ciaddr.s_addr = 0;
996 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000997 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100998 else
Simon Kelley44a2a312004-03-10 20:04:35 +0000999 {
Simon Kelley4011c4e2006-10-28 16:26:19 +01001000 if (do_classes)
1001 {
1002 lease->changed = 1;
1003 /* copy user-class and vendor class into new lease, for the script */
1004 if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
1005 {
1006 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001007 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelley4011c4e2006-10-28 16:26:19 +01001008 /* If the user-class option started as counted strings, the first byte will be zero. */
1009 if (len != 0 && ucp[0] == 0)
1010 ucp++, len--;
Simon Kelley5aabfc72007-08-29 11:24:47 +01001011 free(lease->userclass);
1012 if ((lease->userclass = whine_malloc(len+1)))
Simon Kelley4011c4e2006-10-28 16:26:19 +01001013 {
1014 memcpy(lease->userclass, ucp, len);
1015 lease->userclass[len] = 0;
1016 lease->userclass_len = len+1;
1017 }
1018 }
1019 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
1020 {
1021 int len = option_len(opt);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001022 unsigned char *ucp = option_ptr(opt, 0);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001023 free(lease->vendorclass);
1024 if ((lease->vendorclass = whine_malloc(len+1)))
Simon Kelley4011c4e2006-10-28 16:26:19 +01001025 {
1026 memcpy(lease->vendorclass, ucp, len);
1027 lease->vendorclass[len] = 0;
1028 lease->vendorclass_len = len+1;
1029 }
1030 }
1031 }
1032
Simon Kelley5aabfc72007-08-29 11:24:47 +01001033 if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
Simon Kelley4011c4e2006-10-28 16:26:19 +01001034 {
Simon Kelleyb8187c82005-11-26 21:46:27 +00001035 hostname = client_hostname;
1036 hostname_auth = 1;
1037 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001038
Simon Kelleycdeda282006-03-16 20:16:06 +00001039 if (context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001040 {
1041 context->netid.next = netid;
1042 netid = &context->netid;
1043 }
1044
Simon Kelley824af852008-02-12 20:43:05 +00001045 time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
Simon Kelleycdeda282006-03-16 20:16:06 +00001046 lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
Simon Kelley832af0b2007-01-21 20:01:28 +00001047
1048 /* if all the netids in the ignore_name list are present, ignore client-supplied name */
1049 if (!hostname_auth)
1050 {
1051 for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
1052 if ((!id_list->list) || match_netid(id_list->list, netid, 0))
1053 break;
1054 if (id_list)
1055 hostname = NULL;
1056 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001057 if (hostname)
Simon Kelley9009d742008-11-14 20:04:27 +00001058 lease_set_hostname(lease, hostname, hostname_auth);
Simon Kelley832af0b2007-01-21 20:01:28 +00001059
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001060 lease_set_expires(lease, time, now);
Simon Kelley824af852008-02-12 20:43:05 +00001061 lease_set_interface(lease, int_index);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001062
1063 if (override.s_addr != 0)
1064 lease->override = override;
1065 else
1066 override = lease->override;
1067
Simon Kelley5aabfc72007-08-29 11:24:47 +01001068 log_packet("ACK", &mess->yiaddr, emac, emac_len, iface_name, hostname);
Simon Kelley832af0b2007-01-21 20:01:28 +00001069
Simon Kelley9e038942008-05-30 20:06:34 +01001070 clear_packet(mess, end, agent_id);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001071 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001072 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001073 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley59353a62004-11-21 19:34:28 +00001074 if (time != 0xffffffff)
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001075 {
Simon Kelley59353a62004-11-21 19:34:28 +00001076 while (fuzz > (time/16))
1077 fuzz = fuzz/2;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001078 option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
1079 option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001080 }
Simon Kelley9009d742008-11-14 20:04:27 +00001081 do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
1082 domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley44a2a312004-03-10 20:04:35 +00001083 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001084
Simon Kelley5aabfc72007-08-29 11:24:47 +01001085 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001086
1087 case DHCPINFORM:
Simon Kelley26128d22004-11-14 16:43:54 +00001088 if (ignore || have_config(config, CONFIG_DISABLE))
Simon Kelleyb8187c82005-11-26 21:46:27 +00001089 message = _("ignored");
Simon Kelley33820b72004-04-03 21:10:00 +01001090
Simon Kelley5aabfc72007-08-29 11:24:47 +01001091 log_packet("INFORM", &mess->ciaddr, emac, emac_len, iface_name, message);
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001092
Simon Kelley73a08a22009-02-05 20:28:08 +00001093 if (message || mess->ciaddr.s_addr == 0)
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001094 return 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001095
1096 /* For DHCPINFORM only, cope without a valid context */
1097 context = narrow_context(context, mess->ciaddr, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001098
Simon Kelley5aabfc72007-08-29 11:24:47 +01001099 /* Find a least based on IP address if we didn't
1100 get one from MAC address/client-d */
1101 if (!lease &&
1102 (lease = lease_find_by_addr(mess->ciaddr)) &&
1103 lease->hostname)
1104 hostname = lease->hostname;
1105
1106 if (!hostname)
1107 hostname = host_from_dns(mess->ciaddr);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001108
Simon Kelley5aabfc72007-08-29 11:24:47 +01001109 log_packet("ACK", &mess->ciaddr, emac, emac_len, iface_name, hostname);
1110
Simon Kelley73a08a22009-02-05 20:28:08 +00001111 if (context && context->netid.net)
Simon Kelley59353a62004-11-21 19:34:28 +00001112 {
1113 context->netid.next = netid;
1114 netid = &context->netid;
1115 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001116
Simon Kelley3927da42008-07-20 15:10:39 +01001117 if (lease)
1118 {
1119 if (override.s_addr != 0)
1120 lease->override = override;
1121 else
1122 override = lease->override;
1123 }
1124
Simon Kelley9e038942008-05-30 20:06:34 +01001125 clear_packet(mess, end, agent_id);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001126 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
Simon Kelley73a08a22009-02-05 20:28:08 +00001127 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
1128
Simon Kelley5aabfc72007-08-29 11:24:47 +01001129 if (lease)
1130 {
1131 if (lease->expires == 0)
1132 time = 0xffffffff;
1133 else
1134 time = (unsigned int)difftime(lease->expires, now);
1135 option_put(mess, end, OPTION_LEASE_TIME, 4, time);
Simon Kelley824af852008-02-12 20:43:05 +00001136 lease_set_interface(lease, int_index);
Simon Kelley5aabfc72007-08-29 11:24:47 +01001137 }
Simon Kelley824af852008-02-12 20:43:05 +00001138
Simon Kelley9009d742008-11-14 20:04:27 +00001139 do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
1140 domain, netid, subnet_addr, fqdn_flags, borken_opt, agent_id);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001141
Simon Kelley5aabfc72007-08-29 11:24:47 +01001142 *is_inform = 1; /* handle reply differently */
1143 return dhcp_packet_size(mess, netid);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001144 }
1145
1146 return 0;
1147}
1148
Simon Kelley6b010842007-02-12 20:32:07 +00001149/* find a good value to use as MAC address for logging and address-allocation hashing.
1150 This is normally just the chaddr field from the DHCP packet,
1151 but eg Firewire will have hlen == 0 and use the client-id instead.
1152 This could be anything, but will normally be EUI64 for Firewire.
1153 We assume that if the first byte of the client-id equals the htype byte
1154 then the client-id is using the usual encoding and use the rest of the
1155 client-id: if not we can use the whole client-id. This should give
1156 sane MAC address logs. */
Simon Kelley9009d742008-11-14 20:04:27 +00001157unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
Simon Kelley6b010842007-02-12 20:32:07 +00001158 int clid_len, unsigned char *clid, int *len_out)
1159{
1160 if (hwlen == 0 && clid && clid_len > 3)
1161 {
1162 if (clid[0] == hwtype)
1163 {
1164 *len_out = clid_len - 1 ;
1165 return clid + 1;
1166 }
1167
1168#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
1169 if (clid[0] == ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
1170 {
1171 *len_out = clid_len - 1 ;
1172 return clid + 1;
1173 }
1174#endif
1175
1176 *len_out = clid_len;
1177 return clid;
1178 }
1179
1180 *len_out = hwlen;
1181 return hwaddr;
1182}
1183
Simon Kelley824af852008-02-12 20:43:05 +00001184static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001185{
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001186 unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
Simon Kelleycdeda282006-03-16 20:16:06 +00001187
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001188 if (opt)
1189 {
1190 unsigned int req_time = option_uint(opt, 4);
1191 if (req_time < 120 )
1192 req_time = 120; /* sanity */
1193 if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
1194 time = req_time;
1195 }
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001196
1197 return time;
1198}
1199
Simon Kelley73a08a22009-02-05 20:28:08 +00001200static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001201{
Simon Kelley73a08a22009-02-05 20:28:08 +00001202 if (override.s_addr != 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001203 return override;
Simon Kelley73a08a22009-02-05 20:28:08 +00001204 else if (context)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001205 return context->local;
Simon Kelley73a08a22009-02-05 20:28:08 +00001206 else
1207 return fallback;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001208}
1209
Simon Kelleyf2621c72007-04-29 19:47:21 +01001210static int sanitise(unsigned char *opt, char *buf)
1211{
1212 char *p;
1213 int i;
1214
1215 *buf = 0;
1216
1217 if (!opt)
1218 return 0;
1219
Simon Kelley1a6bca82008-07-11 11:11:42 +01001220 p = option_ptr(opt, 0);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001221
1222 for (i = option_len(opt); i > 0; i--)
1223 {
1224 char c = *p++;
Simon Kelley824af852008-02-12 20:43:05 +00001225 if (isprint((int)c))
Simon Kelleyf2621c72007-04-29 19:47:21 +01001226 *buf++ = c;
1227 }
1228 *buf = 0; /* add terminator */
1229
1230 return 1;
1231}
1232
Simon Kelley5aabfc72007-08-29 11:24:47 +01001233static void log_packet(char *type, void *addr, unsigned char *ext_mac,
1234 int mac_len, char *interface, char *string)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001235{
Simon Kelley16972692006-10-16 20:04:18 +01001236 struct in_addr a;
1237
1238 /* addr may be misaligned */
1239 if (addr)
1240 memcpy(&a, addr, sizeof(a));
1241
Simon Kelleyf2621c72007-04-29 19:47:21 +01001242 my_syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
1243 type ? "DHCP" : "BOOTP",
1244 type ? type : "",
1245 interface,
1246 addr ? inet_ntoa(a) : "",
1247 addr ? " " : "",
Simon Kelley5aabfc72007-08-29 11:24:47 +01001248 print_mac(daemon->namebuff, ext_mac, mac_len),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001249 string ? string : "");
1250}
1251
Simon Kelley5aabfc72007-08-29 11:24:47 +01001252static void log_options(unsigned char *start)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001253{
1254 while (*start != OPTION_END)
1255 {
1256 char *text = option_string(start[0]);
1257 unsigned char trunc = start[1] < 13 ? start[1] : 13;
1258 my_syslog(LOG_INFO, "sent size:%3d option:%3d%s%s%s%s%s",
1259 start[1], start[0],
1260 text ? ":" : "", text ? text : "",
1261 start[1] == 0 ? "" : " ",
Simon Kelley5aabfc72007-08-29 11:24:47 +01001262 start[1] == 0 ? "" : print_mac(daemon->namebuff, &start[2], trunc),
Simon Kelleyf2621c72007-04-29 19:47:21 +01001263 trunc == start[1] ? "" : "...");
1264 start += start[1] + 2;
1265 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001266}
1267
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001268static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001269{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001270 while (1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001271 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001272 if (p > end)
1273 return NULL;
1274 else if (*p == OPTION_END)
1275 return opt == OPTION_END ? p : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001276 else if (*p == OPTION_PAD)
1277 p++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001278 else
1279 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001280 int opt_len;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001281 if (p > end - 2)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001282 return NULL; /* malformed packet */
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001283 opt_len = option_len(p);
Simon Kelley1a6bca82008-07-11 11:11:42 +01001284 if (p > end - (2 + opt_len))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001285 return NULL; /* malformed packet */
1286 if (*p == opt && opt_len >= minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001287 return p;
1288 p += opt_len + 2;
1289 }
1290 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001291}
1292
Simon Kelleycdeda282006-03-16 20:16:06 +00001293static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001294{
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001295 unsigned char *ret, *overload;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001296
Simon Kelley3be34542004-09-11 19:12:13 +01001297 /* skip over DHCP cookie; */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001298 if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
1299 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001300
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001301 /* look for overload option. */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001302 if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001303 return NULL;
Simon Kelleybb01cb92004-12-13 20:56:23 +00001304
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001305 /* Can we look in filename area ? */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001306 if ((overload[2] & 1) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001307 (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
1308 return ret;
1309
1310 /* finally try sname area */
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001311 if ((overload[2] & 2) &&
Simon Kelley5e9e0ef2006-04-17 14:24:29 +01001312 (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
1313 return ret;
1314
1315 return NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001316}
1317
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001318static struct in_addr option_addr(unsigned char *opt)
1319{
1320 /* this worries about unaligned data in the option. */
1321 /* struct in_addr is network byte order */
1322 struct in_addr ret;
1323
Simon Kelley1a6bca82008-07-11 11:11:42 +01001324 memcpy(&ret, option_ptr(opt, 0), INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001325
1326 return ret;
1327}
1328
1329static unsigned int option_uint(unsigned char *opt, int size)
1330{
1331 /* this worries about unaligned data and byte order */
1332 unsigned int ret = 0;
1333 int i;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001334 unsigned char *p = option_ptr(opt, 0);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001335
1336 for (i = 0; i < size; i++)
1337 ret = (ret << 8) | *p++;
1338
1339 return ret;
1340}
1341
1342static unsigned char *dhcp_skip_opts(unsigned char *start)
1343{
1344 while (*start != 0)
1345 start += start[1] + 2;
1346 return start;
1347}
1348
1349/* only for use when building packet: doesn't check for bad data. */
1350static unsigned char *find_overload(struct dhcp_packet *mess)
1351{
1352 unsigned char *p = &mess->options[0] + sizeof(u32);
1353
1354 while (*p != 0)
1355 {
1356 if (*p == OPTION_OVERLOAD)
1357 return p;
1358 p += p[1] + 2;
1359 }
1360 return NULL;
1361}
1362
Simon Kelley5aabfc72007-08-29 11:24:47 +01001363static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001364{
1365 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1366 unsigned char *overload;
1367 size_t ret;
Simon Kelley824af852008-02-12 20:43:05 +00001368 struct dhcp_netid_list *id_list;
Simon Kelley9009d742008-11-14 20:04:27 +00001369 struct dhcp_netid *n;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001370
1371 /* We do logging too */
1372 if (netid && (daemon->options & OPT_LOG_OPTS))
1373 {
1374 char *p = daemon->namebuff;
1375 *p = 0;
1376 for (; netid; netid = netid->next)
1377 {
Simon Kelley9009d742008-11-14 20:04:27 +00001378 /* kill dupes. */
1379 for (n = netid->next; n; n = n->next)
1380 if (strcmp(netid->net, n->net) == 0)
1381 break;
1382
1383 if (!n)
1384 {
1385 strncat (p, netid->net, MAXDNAME);
1386 if (netid->next)
1387 strncat (p, ", ", MAXDNAME);
1388 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001389 }
1390 p[MAXDNAME - 1] = 0;
1391 my_syslog(LOG_INFO, _("tags: %s"), p);
1392 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001393
1394 /* add END options to the regions. */
1395 if ((overload = find_overload(mess)))
1396 {
1397 if (option_uint(overload, 1) & 1)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001398 {
1399 *dhcp_skip_opts(mess->file) = OPTION_END;
1400 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001401 log_options(mess->file);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001402 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001403 if (option_uint(overload, 1) & 2)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001404 {
1405 *dhcp_skip_opts(mess->sname) = OPTION_END;
1406 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001407 log_options(mess->sname);
Simon Kelleyf2621c72007-04-29 19:47:21 +01001408 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001409 }
1410
1411 *p++ = OPTION_END;
Simon Kelley824af852008-02-12 20:43:05 +00001412
Simon Kelleyf2621c72007-04-29 19:47:21 +01001413 if (daemon->options & OPT_LOG_OPTS)
Simon Kelley5aabfc72007-08-29 11:24:47 +01001414 log_options(&mess->options[0] + sizeof(u32));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001415
Simon Kelley824af852008-02-12 20:43:05 +00001416 for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
1417 if (match_netid(id_list->list, netid, 0))
1418 mess->flags |= htons(0x8000); /* force broadcast */
1419
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001420 ret = (size_t)(p - (unsigned char *)mess);
1421
1422 if (ret < MIN_PACKETSZ)
1423 ret = MIN_PACKETSZ;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001424
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001425 return ret;
1426}
1427
1428static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
1429{
1430 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1431
1432 if (p + len + 3 >= end)
1433 /* not enough space in options area, try and use overload, if poss */
1434 {
1435 unsigned char *overload;
1436
1437 if (!(overload = find_overload(mess)) &&
1438 (mess->file[0] == 0 || mess->sname[0] == 0))
1439 {
1440 /* attempt to overload fname and sname areas, we've reserved space for the
1441 overflow option previuously. */
1442 overload = p;
1443 *(p++) = OPTION_OVERLOAD;
1444 *(p++) = 1;
1445 }
1446
1447 p = NULL;
1448
1449 /* using filename field ? */
1450 if (overload)
1451 {
1452 if (mess->file[0] == 0)
1453 overload[2] |= 1;
1454
1455 if (overload[2] & 1)
1456 {
1457 p = dhcp_skip_opts(mess->file);
1458 if (p + len + 3 >= mess->file + sizeof(mess->file))
1459 p = NULL;
1460 }
1461
1462 if (!p)
1463 {
1464 /* try to bring sname into play (it may be already) */
1465 if (mess->sname[0] == 0)
1466 overload[2] |= 2;
1467
1468 if (overload[2] & 2)
1469 {
1470 p = dhcp_skip_opts(mess->sname);
1471 if (p + len + 3 >= mess->sname + sizeof(mess->file))
1472 p = NULL;
1473 }
1474 }
1475 }
1476
1477 if (!p)
Simon Kelleyf2621c72007-04-29 19:47:21 +01001478 my_syslog(LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001479 }
1480
1481 if (p)
1482 {
1483 *(p++) = opt;
1484 *(p++) = len;
1485 }
1486
1487 return p;
1488}
1489
1490static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
1491{
1492 int i;
1493 unsigned char *p = free_space(mess, end, opt, len);
1494
1495 if (p)
1496 for (i = 0; i < len; i++)
1497 *(p++) = val >> (8 * (len - (i + 1)));
1498}
1499
1500static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,
1501 char *string, int null_term)
1502{
1503 unsigned char *p;
1504 size_t len = strlen(string);
1505
1506 if (null_term && len != 255)
1507 len++;
1508
1509 if ((p = free_space(mess, end, opt, len)))
1510 memcpy(p, string, len);
1511}
1512
1513/* return length, note this only does the data part */
Simon Kelley73a08a22009-02-05 20:28:08 +00001514static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001515{
1516 int len = opt->len;
1517
1518 if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
1519 len++;
1520
1521 if (p && len != 0)
1522 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001523 if (context && (opt->flags & DHOPT_ADDR))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001524 {
1525 int j;
1526 struct in_addr *a = (struct in_addr *)opt->val;
1527 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
1528 {
1529 /* zero means "self" (but not in vendorclass options.) */
1530 if (a->s_addr == 0)
Simon Kelley73a08a22009-02-05 20:28:08 +00001531 memcpy(p, &context->local, INADDRSZ);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001532 else
1533 memcpy(p, a, INADDRSZ);
1534 p += INADDRSZ;
1535 }
1536 }
1537 else
1538 memcpy(p, opt->val, len);
1539 }
1540 return len;
1541}
1542
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001543static int in_list(unsigned char *list, int opt)
1544{
1545 int i;
Simon Kelley6b010842007-02-12 20:32:07 +00001546
1547 /* If no requested options, send everything, not nothing. */
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001548 if (!list)
1549 return 1;
1550
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001551 for (i = 0; list[i] != OPTION_END; i++)
1552 if (opt == list[i])
1553 return 1;
1554
1555 return 0;
1556}
1557
Simon Kelleya2226412004-05-13 20:27:08 +01001558static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001559{
Simon Kelley91dccd02005-03-31 17:48:32 +01001560 struct dhcp_opt *tmp;
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001561 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelley73a08a22009-02-05 20:28:08 +00001562 if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)))
Simon Kelleycdeda282006-03-16 20:16:06 +00001563 if (match_netid(tmp->netid, netid, 1) || match_netid(tmp->netid, netid, 0))
1564 return tmp;
Simon Kelleya2226412004-05-13 20:27:08 +01001565
Simon Kelleya84fa1d2004-04-23 22:21:21 +01001566 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001567}
1568
Simon Kelley6b010842007-02-12 20:32:07 +00001569/* mark vendor-encapsulated options which match the client-supplied or
1570 config-supplied vendor class */
1571static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
1572{
1573 for (; dopt; dopt = dopt->next)
1574 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001575 dopt->flags &= ~(DHOPT_ENCAP_MATCH | DHOPT_ENCAP_DONE);
1576 if (opt && (dopt->flags & DHOPT_VENDOR))
Simon Kelley6b010842007-02-12 20:32:07 +00001577 {
1578 int i, len = 0;
Simon Kelley73a08a22009-02-05 20:28:08 +00001579 if (dopt->u.vendor_class)
1580 len = strlen((char *)dopt->u.vendor_class);
Simon Kelley6b010842007-02-12 20:32:07 +00001581 for (i = 0; i <= (option_len(opt) - len); i++)
Simon Kelley73a08a22009-02-05 20:28:08 +00001582 if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
Simon Kelley6b010842007-02-12 20:32:07 +00001583 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001584 dopt->flags |= DHOPT_ENCAP_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001585 break;
1586 }
1587 }
1588 }
1589}
1590
Simon Kelley73a08a22009-02-05 20:28:08 +00001591static void do_encap_opts(int encap, struct dhcp_packet *mess, unsigned char *end, int null_term)
1592{
1593 int len, enc_len;
1594 struct dhcp_opt *opt, *start;
1595 unsigned char *p;
1596
1597 /* find size in advance */
1598 for (enc_len = 0, start = opt = daemon->dhcp_opts; opt; opt = opt->next)
1599 if (opt->flags & DHOPT_ENCAP_MATCH)
1600 {
1601 int new = do_opt(opt, NULL, NULL, null_term) + 2;
1602 if (enc_len + new <= 255)
1603 enc_len += new;
1604 else
1605 {
1606 p = free_space(mess, end, encap, enc_len);
1607 for (; start && start != opt; start = start->next)
1608 if (p && (start->flags & DHOPT_ENCAP_MATCH))
1609 {
1610 len = do_opt(start, p + 2, NULL, null_term);
1611 *(p++) = start->opt;
1612 *(p++) = len;
1613 p += len;
1614 }
1615 enc_len = new;
1616 start = opt;
1617 }
1618 }
1619
1620 if (enc_len != 0 &&
1621 (p = free_space(mess, end, encap, enc_len + 1)))
1622 {
1623 for (; start; start = start->next)
1624 if (start->flags & DHOPT_ENCAP_MATCH)
1625 {
1626 len = do_opt(start, p + 2, NULL, null_term);
1627 *(p++) = start->opt;
1628 *(p++) = len;
1629 p += len;
1630 }
1631 *p = OPTION_END;
1632 }
1633}
1634
Simon Kelley9e038942008-05-30 20:06:34 +01001635static void clear_packet(struct dhcp_packet *mess, unsigned char *end, unsigned char *agent_id)
Simon Kelley91dccd02005-03-31 17:48:32 +01001636{
Simon Kelley9e038942008-05-30 20:06:34 +01001637 /* don't clear agent_id */
1638 if (agent_id)
1639 end = agent_id;
1640
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001641 memset(mess->sname, 0, sizeof(mess->sname));
1642 memset(mess->file, 0, sizeof(mess->file));
1643 memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
1644 mess->siaddr.s_addr = 0;
1645}
Simon Kelleycdeda282006-03-16 20:16:06 +00001646
Simon Kelley9e038942008-05-30 20:06:34 +01001647static void restore_agent_id(unsigned char *agent_id, struct dhcp_packet *mess, unsigned char *real_end)
1648{
1649 if (agent_id)
1650 {
1651 unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
1652 memmove(p, agent_id, real_end - agent_id);
1653 p += real_end - agent_id;
1654 memset(p, 0, real_end - p); /* in case of overlap */
1655 }
1656}
1657
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001658static void do_options(struct dhcp_context *context,
1659 struct dhcp_packet *mess,
1660 unsigned char *real_end,
1661 unsigned char *req_options,
Simon Kelley9009d742008-11-14 20:04:27 +00001662 char *hostname,
1663 char *domain, char *config_domain,
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001664 struct dhcp_netid *netid,
1665 struct in_addr subnet_addr,
1666 unsigned char fqdn_flags,
1667 int null_term,
1668 unsigned char *agent_id)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001669{
Simon Kelley3be34542004-09-11 19:12:13 +01001670 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001671 struct dhcp_boot *boot;
1672 unsigned char *p, *end = agent_id ? agent_id : real_end;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001673 int i, len, force_encap = 0;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001674 unsigned char f0 = 0, s0 = 0;
Simon Kelley824af852008-02-12 20:43:05 +00001675 int done_file = 0, done_server = 0;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001676
Simon Kelley9009d742008-11-14 20:04:27 +00001677 if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
1678 my_syslog(LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
1679
Simon Kelleyf2621c72007-04-29 19:47:21 +01001680 /* logging */
1681 if ((daemon->options & OPT_LOG_OPTS) && req_options)
1682 {
1683 char *q = daemon->namebuff;
1684 for (i = 0; req_options[i] != OPTION_END; i++)
1685 {
1686 char *s = option_string(req_options[i]);
Simon Kelley824af852008-02-12 20:43:05 +00001687 q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
1688 "%d%s%s%s",
1689 req_options[i],
1690 s ? ":" : "",
1691 s ? s : "",
1692 req_options[i+1] == OPTION_END ? "" : ", ");
Simon Kelleyf2621c72007-04-29 19:47:21 +01001693 if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
1694 {
1695 q = daemon->namebuff;
1696 my_syslog(LOG_INFO, _("requested options: %s"), daemon->namebuff);
1697 }
1698 }
1699 }
1700
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001701 /* decide which dhcp-boot option we're using */
1702 for (boot = daemon->boot_config; boot; boot = boot->next)
1703 if (match_netid(boot->netid, netid, 0))
1704 break;
1705 if (!boot)
1706 /* No match, look for one without a netid */
1707 for (boot = daemon->boot_config; boot; boot = boot->next)
1708 if (match_netid(boot->netid, netid, 1))
1709 break;
Simon Kelley1ab84e22004-01-29 16:48:35 +00001710
Simon Kelley73a08a22009-02-05 20:28:08 +00001711 if (context)
1712 mess->siaddr = context->local;
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001713
1714 /* See if we can send the boot stuff as options.
1715 To do this we need a requested option list, BOOTP
Simon Kelley824af852008-02-12 20:43:05 +00001716 and very old DHCP clients won't have this, we also
1717 provide an manual option to disable it.
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001718 Some PXE ROMs have bugs (surprise!) and need zero-terminated
1719 names, so we always send those. */
1720 if (boot)
1721 {
1722 if (boot->sname)
Simon Kelley824af852008-02-12 20:43:05 +00001723 {
1724 if (!(daemon->options & OPT_NO_OVERRIDE) &&
1725 req_options &&
1726 in_list(req_options, OPTION_SNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001727 option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
1728 else
Simon Kelley824af852008-02-12 20:43:05 +00001729 strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001730 }
1731
1732 if (boot->file)
1733 {
Simon Kelley824af852008-02-12 20:43:05 +00001734 if (!(daemon->options & OPT_NO_OVERRIDE) &&
1735 req_options &&
1736 in_list(req_options, OPTION_FILENAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001737 option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
1738 else
Simon Kelley824af852008-02-12 20:43:05 +00001739 strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001740 }
1741
1742 if (boot->next_server.s_addr)
1743 mess->siaddr = boot->next_server;
Simon Kelleyf2621c72007-04-29 19:47:21 +01001744
1745 if (daemon->options & OPT_LOG_OPTS)
1746 my_syslog(LOG_INFO, _("next server: %s"), inet_ntoa(mess->siaddr));
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001747 }
Simon Kelley824af852008-02-12 20:43:05 +00001748 else
1749 /* Use the values of the relevant options if no dhcp-boot given and
1750 they're no explicitly asked for as options. */
1751 {
1752 if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
1753 (opt = option_find2(netid, config_opts, OPTION_FILENAME)))
1754 {
1755 strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
1756 done_file = 1;
1757 }
1758
1759 if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
1760 (opt = option_find2(netid, config_opts, OPTION_SNAME)))
1761 {
1762 strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
1763 done_server = 1;
1764 }
1765 }
Simon Kelleyf2621c72007-04-29 19:47:21 +01001766
Simon Kelley824af852008-02-12 20:43:05 +00001767 if (daemon->options & OPT_LOG_OPTS)
1768 {
1769 if (strlen((char *)mess->file) != 0)
1770 my_syslog(LOG_INFO, _("bootfile name: %s"), (char *)mess->file);
1771
1772 if (strlen((char *)mess->sname) != 0)
1773 my_syslog(LOG_INFO, _("server name: %s"), (char *)mess->sname);
1774 }
1775
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001776 /* We don't want to do option-overload for BOOTP, so make the file and sname
1777 fields look like they are in use, even when they aren't. This gets restored
1778 at the end of this function. */
1779
Simon Kelley824af852008-02-12 20:43:05 +00001780 if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001781 {
1782 f0 = mess->file[0];
1783 mess->file[0] = 1;
1784 s0 = mess->sname[0];
1785 mess->sname[0] = 1;
1786 }
1787
1788 /* At this point, if mess->sname or mess->file are zeroed, they are available
1789 for option overload, reserve space for the overload option. */
1790 if (mess->file[0] == 0 || mess->sname[0] == 0)
1791 end -= 3;
1792
Simon Kelley3be34542004-09-11 19:12:13 +01001793 /* rfc3011 says this doesn't need to be in the requested options list. */
1794 if (subnet_addr.s_addr)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001795 option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
Simon Kelley73a08a22009-02-05 20:28:08 +00001796
1797 /* replies to DHCPINFORM may not have a valid context */
1798 if (context)
1799 {
1800 if (!option_find2(netid, config_opts, OPTION_NETMASK))
1801 option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
1802
1803 /* May not have a "guessed" broadcast address if we got no packets via a relay
1804 from this net yet (ie just unicast renewals after a restart */
1805 if (context->broadcast.s_addr &&
1806 !option_find2(netid, config_opts, OPTION_BROADCAST))
1807 option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
1808
1809 /* Same comments as broadcast apply, and also may not be able to get a sensible
1810 default when using subnet select. User must configure by steam in that case. */
1811 if (context->router.s_addr &&
1812 in_list(req_options, OPTION_ROUTER) &&
1813 !option_find2(netid, config_opts, OPTION_ROUTER))
1814 option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
1815
1816 if (in_list(req_options, OPTION_DNSSERVER) &&
1817 !option_find2(netid, config_opts, OPTION_DNSSERVER))
1818 option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
1819 }
Simon Kelley3be34542004-09-11 19:12:13 +01001820
Simon Kelley9009d742008-11-14 20:04:27 +00001821 if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +01001822 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelley9009d742008-11-14 20:04:27 +00001823 option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001824
Simon Kelley824af852008-02-12 20:43:05 +00001825 /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
Simon Kelley3d8df262005-08-29 12:19:27 +01001826 if (hostname)
1827 {
Simon Kelley824af852008-02-12 20:43:05 +00001828 if (in_list(req_options, OPTION_HOSTNAME) &&
1829 !option_find2(netid, config_opts, OPTION_HOSTNAME))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001830 option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
Simon Kelley3d8df262005-08-29 12:19:27 +01001831
1832 if (fqdn_flags != 0)
1833 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001834 len = strlen(hostname) + 3;
1835
Simon Kelley3d8df262005-08-29 12:19:27 +01001836 if (fqdn_flags & 0x04)
1837 len += 2;
Simon Kelleycdeda282006-03-16 20:16:06 +00001838 else if (null_term)
1839 len++;
1840
Simon Kelley9009d742008-11-14 20:04:27 +00001841 if (domain)
1842 len += strlen(domain) + 1;
Simon Kelley3d8df262005-08-29 12:19:27 +01001843
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001844 if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
Simon Kelley3d8df262005-08-29 12:19:27 +01001845 {
Simon Kelley3d8df262005-08-29 12:19:27 +01001846 *(p++) = fqdn_flags;
1847 *(p++) = 255;
1848 *(p++) = 255;
1849
1850 if (fqdn_flags & 0x04)
1851 {
1852 p = do_rfc1035_name(p, hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00001853 if (domain)
1854 p = do_rfc1035_name(p, domain);
Simon Kelley3d8df262005-08-29 12:19:27 +01001855 *p++ = 0;
1856 }
1857 else
1858 {
1859 memcpy(p, hostname, strlen(hostname));
1860 p += strlen(hostname);
Simon Kelley9009d742008-11-14 20:04:27 +00001861 if (domain)
Simon Kelley3d8df262005-08-29 12:19:27 +01001862 {
1863 *(p++) = '.';
Simon Kelley9009d742008-11-14 20:04:27 +00001864 memcpy(p, domain, strlen(domain));
1865 p += strlen(domain);
Simon Kelleycdeda282006-03-16 20:16:06 +00001866 }
1867 if (null_term)
1868 *(p++) = 0;
Simon Kelley3d8df262005-08-29 12:19:27 +01001869 }
1870 }
1871 }
1872 }
1873
Simon Kelley6b010842007-02-12 20:32:07 +00001874 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001875 {
Simon Kelley824af852008-02-12 20:43:05 +00001876 int optno = opt->opt;
1877
Simon Kelley6b010842007-02-12 20:32:07 +00001878 /* was it asked for, or are we sending it anyway? */
Simon Kelley824af852008-02-12 20:43:05 +00001879 if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
Simon Kelley6b010842007-02-12 20:32:07 +00001880 continue;
1881
1882 /* prohibit some used-internally options */
Simon Kelley824af852008-02-12 20:43:05 +00001883 if (optno == OPTION_CLIENT_FQDN ||
1884 optno == OPTION_MAXMESSAGE ||
1885 optno == OPTION_OVERLOAD ||
1886 optno == OPTION_PAD ||
1887 optno == OPTION_END)
1888 continue;
1889
1890 if (optno == OPTION_SNAME && done_server)
1891 continue;
1892
1893 if (optno == OPTION_FILENAME && done_file)
Simon Kelley6b010842007-02-12 20:32:07 +00001894 continue;
1895
1896 /* netids match and not encapsulated? */
Simon Kelley824af852008-02-12 20:43:05 +00001897 if (opt != option_find2(netid, config_opts, optno))
Simon Kelley33820b72004-04-03 21:10:00 +01001898 continue;
1899
1900 /* For the options we have default values on
1901 dhc-option=<optionno> means "don't include this option"
1902 not "include a zero-length option" */
1903 if (opt->len == 0 &&
Simon Kelley824af852008-02-12 20:43:05 +00001904 (optno == OPTION_NETMASK ||
1905 optno == OPTION_BROADCAST ||
1906 optno == OPTION_ROUTER ||
1907 optno == OPTION_DNSSERVER ||
1908 optno == OPTION_DOMAINNAME ||
1909 optno == OPTION_HOSTNAME))
Simon Kelley33820b72004-04-03 21:10:00 +01001910 continue;
Simon Kelley824af852008-02-12 20:43:05 +00001911
1912 /* always force null-term for filename ans servername - buggy PXE again. */
Simon Kelley73a08a22009-02-05 20:28:08 +00001913 len = do_opt(opt, NULL, context,
Simon Kelley824af852008-02-12 20:43:05 +00001914 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
Simon Kelley91dccd02005-03-31 17:48:32 +01001915
Simon Kelley824af852008-02-12 20:43:05 +00001916 if ((p = free_space(mess, end, optno, len)))
Simon Kelley6b010842007-02-12 20:32:07 +00001917 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001918 do_opt(opt, p, context,
Simon Kelley824af852008-02-12 20:43:05 +00001919 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
1920
Simon Kelley6b010842007-02-12 20:32:07 +00001921 /* If we send a vendor-id, revisit which vendor-ops we consider
1922 it appropriate to send. */
Simon Kelley824af852008-02-12 20:43:05 +00001923 if (optno == OPTION_VENDOR_ID)
Simon Kelley6b010842007-02-12 20:32:07 +00001924 match_vendor_opts(p - 2, config_opts);
1925 }
1926 }
1927
Simon Kelley73a08a22009-02-05 20:28:08 +00001928 /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
Simon Kelley6b010842007-02-12 20:32:07 +00001929 for (opt = config_opts; opt; opt = opt->next)
Simon Kelley73a08a22009-02-05 20:28:08 +00001930 if (opt->flags & DHOPT_ENCAP_MATCH)
Simon Kelley6b010842007-02-12 20:32:07 +00001931 {
1932 if (!match_netid(opt->netid, netid, 1) && !match_netid(opt->netid, netid, 0))
Simon Kelley73a08a22009-02-05 20:28:08 +00001933 opt->flags &= ~DHOPT_ENCAP_MATCH;
Simon Kelley6b010842007-02-12 20:32:07 +00001934 else if (opt->flags & DHOPT_FORCE)
1935 force_encap = 1;
1936 }
1937
1938 if (force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT))
Simon Kelley73a08a22009-02-05 20:28:08 +00001939 do_encap_opts(OPTION_VENDOR_CLASS_OPT, mess, end, null_term);
1940
1941 /* Now send options to be encapsulated in arbitrary options,
1942 eg dhcp-option=encap:172,17,.......
1943 The may be more that one "outer" to do, so group
1944 all the options which match each outer in turn. */
1945 for (opt = config_opts; opt; opt = opt->next)
1946 if ((opt->flags & (DHOPT_ENCAPSULATE | DHOPT_ENCAP_DONE)) == DHOPT_ENCAPSULATE)
1947 {
1948 struct dhcp_opt *o;
1949 int found = 0;
1950
1951 for (o = config_opts; o; o = o->next)
Simon Kelley91dccd02005-03-31 17:48:32 +01001952 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001953 o->flags &= ~DHOPT_ENCAP_MATCH;
1954 if ((o->flags & DHOPT_ENCAPSULATE) && opt->u.encap == o->u.encap)
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001955 {
Simon Kelley73a08a22009-02-05 20:28:08 +00001956 o->flags |= DHOPT_ENCAP_DONE;
1957 if ((match_netid(o->netid, netid, 1) || match_netid(o->netid, netid, 0)) &&
1958 (o->flags & DHOPT_FORCE || in_list(req_options, o->u.encap)))
1959 {
1960 o->flags |= DHOPT_ENCAP_MATCH;
1961 found = 1;
1962 }
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001963 }
Simon Kelley91dccd02005-03-31 17:48:32 +01001964 }
Simon Kelley73a08a22009-02-05 20:28:08 +00001965
1966 if (found)
1967 do_encap_opts(opt->u.encap, mess, end, null_term);
1968 }
1969
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001970 /* move agent_id back down to the end of the packet */
Simon Kelley9e038942008-05-30 20:06:34 +01001971 restore_agent_id(agent_id, mess, real_end);
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001972
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001973 /* restore BOOTP anti-overload hack */
Simon Kelley824af852008-02-12 20:43:05 +00001974 if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
Simon Kelley1b7ecd12007-02-05 14:57:57 +00001975 {
1976 mess->file[0] = f0;
1977 mess->sname[0] = s0;
1978 }
1979}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001980