blob: 27394621f206e66e488a3c4d22b21c2c81d4fbac [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
2
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
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
13/* Author's email: simon@thekelleys.org.uk */
14
15#include "dnsmasq.h"
16
17#define BOOTREQUEST 1
18#define BOOTREPLY 2
19#define DHCP_COOKIE 0x63825363
20
Simon Kelleya2226412004-05-13 20:27:08 +010021/* The Linux in-kernel DHCP client silently ignores any packet
22 smaller than this. Sigh........... */
23#define MIN_PACKETSZ 300
24
Simon Kelley9e4abcb2004-01-22 19:47:41 +000025#define OPTION_PAD 0
26#define OPTION_NETMASK 1
27#define OPTION_ROUTER 3
28#define OPTION_DNSSERVER 6
29#define OPTION_HOSTNAME 12
30#define OPTION_DOMAINNAME 15
31#define OPTION_BROADCAST 28
Simon Kelley9e4abcb2004-01-22 19:47:41 +000032#define OPTION_REQUESTED_IP 50
33#define OPTION_LEASE_TIME 51
34#define OPTION_OVERLOAD 52
35#define OPTION_MESSAGE_TYPE 53
36#define OPTION_SERVER_IDENTIFIER 54
37#define OPTION_REQUESTED_OPTIONS 55
Simon Kelley44a2a312004-03-10 20:04:35 +000038#define OPTION_MESSAGE 56
Simon Kelley9e4abcb2004-01-22 19:47:41 +000039#define OPTION_MAXMESSAGE 57
Simon Kelley44a2a312004-03-10 20:04:35 +000040#define OPTION_T1 58
41#define OPTION_T2 59
Simon Kelleya84fa1d2004-04-23 22:21:21 +010042#define OPTION_VENDOR_ID 60
Simon Kelley44a2a312004-03-10 20:04:35 +000043#define OPTION_CLIENT_ID 61
Simon Kelleya2226412004-05-13 20:27:08 +010044#define OPTION_USER_CLASS 77
Simon Kelley3be34542004-09-11 19:12:13 +010045#define OPTION_SUBNET_SELECT 118
Simon Kelley9e4abcb2004-01-22 19:47:41 +000046#define OPTION_END 255
47
48#define DHCPDISCOVER 1
49#define DHCPOFFER 2
50#define DHCPREQUEST 3
51#define DHCPDECLINE 4
52#define DHCPACK 5
53#define DHCPNAK 6
54#define DHCPRELEASE 7
55#define DHCPINFORM 8
56
57static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
Simon Kelleya2226412004-05-13 20:27:08 +010058static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
Simon Kelley44a2a312004-03-10 20:04:35 +000059static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000060static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
61static int option_len(unsigned char *opt);
62static void *option_ptr(unsigned char *opt);
63static struct in_addr option_addr(unsigned char *opt);
Simon Kelley44a2a312004-03-10 20:04:35 +000064static unsigned int option_uint(unsigned char *opt, int size);
65static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000066static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
67static unsigned char *do_req_options(struct dhcp_context *context,
68 unsigned char *p, unsigned char *end,
69 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +010070 struct daemon *daemon,
71 char *hostname,
Simon Kelley44a2a312004-03-10 20:04:35 +000072 struct in_addr iface_addr,
Simon Kelley3be34542004-09-11 19:12:13 +010073 struct dhcp_netid *netid,
74 struct in_addr subnet_addr);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000075
Simon Kelley33820b72004-04-03 21:10:00 +010076static int have_config(struct dhcp_config *config, unsigned int mask)
77{
78 return config && (config->flags & mask);
79}
Simon Kelley9e4abcb2004-01-22 19:47:41 +000080
Simon Kelley3be34542004-09-11 19:12:13 +010081int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000082{
Simon Kelley36717ee2004-09-20 19:20:58 +010083 struct dhcp_context *context, *context_tmp;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000084 unsigned char *opt, *clid;
Simon Kelley9c74ec02004-08-13 21:13:03 +010085 struct dhcp_lease *lease, *ltmp;
Simon Kelleya2226412004-05-13 20:27:08 +010086 struct dhcp_vendor *vendor;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000087 int clid_len;
Simon Kelley3be34542004-09-11 19:12:13 +010088 struct dhcp_packet *mess = &daemon->dhcp_packet->data;
89 unsigned char *p = mess->options + sizeof(u32); /* skip cookie */
90 unsigned char *end = (unsigned char *)(daemon->dhcp_packet + 1);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000091 char *hostname = NULL;
92 char *req_options = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +000093 char *message = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000094 unsigned int renewal_time, expires_time, def_time;
95 struct dhcp_config *config;
Simon Kelleya2226412004-05-13 20:27:08 +010096 struct dhcp_netid *netid = NULL;
Simon Kelley3be34542004-09-11 19:12:13 +010097 struct in_addr addr, subnet_addr;
Simon Kelleya84fa1d2004-04-23 22:21:21 +010098 unsigned short fuzz = 0;
Simon Kelley3be34542004-09-11 19:12:13 +010099 unsigned int mess_type = 0;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100100
Simon Kelley3be34542004-09-11 19:12:13 +0100101 subnet_addr.s_addr = 0;
102
103 if (mess->op != BOOTREQUEST)
Simon Kelley33820b72004-04-03 21:10:00 +0100104 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000105
Simon Kelley33820b72004-04-03 21:10:00 +0100106 /* Token ring is supported when we have packet sockets
107 to make the HW headers for us. We don't have the code to build
108 token ring headers when using BPF. We rely on the fact that
109 token ring hwaddrs are the same size as ethernet hwaddrs. */
Simon Kelley3be34542004-09-11 19:12:13 +0100110
Simon Kelley33820b72004-04-03 21:10:00 +0100111#ifdef HAVE_BPF
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100112 if (mess->htype != ARPHRD_ETHER)
Simon Kelley33820b72004-04-03 21:10:00 +0100113#else
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100114 if (mess->htype != ARPHRD_ETHER && mess->htype != ARPHRD_IEEE802)
Simon Kelley33820b72004-04-03 21:10:00 +0100115#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100116 {
117 syslog(LOG_WARNING, "DHCP request for unsupported hardware type (%d) recieved on %s",
118 mess->htype, iface_name);
119 return 0;
120 }
Simon Kelley3be34542004-09-11 19:12:13 +0100121
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100122 if (mess->hlen != ETHER_ADDR_LEN)
123 return 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100124
125 /* check for DHCP rather than BOOTP */
126 if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
Simon Kelley44a2a312004-03-10 20:04:35 +0000127 {
Simon Kelley3be34542004-09-11 19:12:13 +0100128 mess_type = option_uint(opt, 1);
Simon Kelley44a2a312004-03-10 20:04:35 +0000129
Simon Kelley3be34542004-09-11 19:12:13 +0100130 /* only insist on a cookie for DHCP. */
131 if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
132 return 0;
133
134 /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
135 it can affect the context-determination code. */
136 if ((option_find(mess, sz, OPTION_REQUESTED_IP) || mess_type == DHCPDISCOVER))
137 mess->ciaddr.s_addr = 0;
138
139 /* Check for RFC3011 subnet selector */
140 if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT)))
141 subnet_addr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000142 }
Simon Kelley3be34542004-09-11 19:12:13 +0100143
144 /* Determine network for this packet. If the machine has an address already, and we don't have
145 have a giaddr or explicit subnet selector, use the ciaddr. This is necessary because a
146 machine which got a lease via a relay won't use the relay to renew. */
147 addr =
148 subnet_addr.s_addr ? subnet_addr :
149 (mess->giaddr.s_addr ? mess->giaddr :
150 (mess->ciaddr.s_addr ? mess->ciaddr : iface_addr));
Simon Kelley36717ee2004-09-20 19:20:58 +0100151
152 /* More than one context may match, we build a chain of them all on ->current
153 Note that if netmasks, netid or lease times don't match, odd things may happen. */
154
155 for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
156 if (context_tmp->netmask.s_addr &&
157 is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
158 is_same_net(addr, context_tmp->end, context_tmp->netmask))
159 {
160 context_tmp->current = context;
161 context = context_tmp;
162
163 /* start to build netid chain */
164 if (context_tmp->netid.net)
165 {
166 context_tmp->netid.next = netid;
167 netid = &context_tmp->netid;
168 }
169 }
Simon Kelley3be34542004-09-11 19:12:13 +0100170
171 if (!context)
172 {
173 syslog(LOG_WARNING, "no address range available for DHCP request %s %s",
174 subnet_addr.s_addr ? "with subnet selector" : "via",
175 subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
176 return 0;
177 }
178
179 mess->op = BOOTREPLY;
Simon Kelley36717ee2004-09-20 19:20:58 +0100180
Simon Kelley3be34542004-09-11 19:12:13 +0100181 if (mess_type == 0)
182 {
183 /* BOOTP request */
184 config = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, NULL);
185 if (have_config(config, CONFIG_ADDR) &&
186 !have_config(config, CONFIG_DISABLE) &&
187 !lease_find_by_addr(config->addr))
188 {
189 struct dhcp_netid id;
190 char save = mess->file[128];
191 end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
192 mess->yiaddr = config->addr;
193 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
194 if (have_config(config, CONFIG_NAME))
195 hostname = config->hostname;
196 if (have_config(config, CONFIG_NETID))
197 {
198 config->netid.next = netid;
199 netid = &config->netid;
200 }
201 /* Match incoming filename field as a netid. */
202 if (mess->file[0])
203 {
204 mess->file[128] = 0; /* ensure zero term. */
205 id.net = mess->file;
206 id.next = netid;
207 netid = &id;
208 }
209 p = do_req_options(context, p, end, NULL, daemon,
210 hostname, iface_addr, netid, subnet_addr);
211 /* must do this after do_req_options since it overwrites filename field. */
212 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
213 p = option_end(p, end, mess);
214 log_packet(NULL, &config->addr, mess->chaddr, iface_name, NULL);
215 mess->file[128] = save;
216 return p - (unsigned char *)mess;
217 }
218 return 0;
219 }
220
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000221 /* If there is no client identifier option, use the hardware address */
222 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
223 {
224 clid = option_ptr(opt);
225 clid_len = option_len(opt);
226 }
227 else
228 {
229 clid = mess->chaddr;
230 clid_len = 0;
231 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100232
Simon Kelley3be34542004-09-11 19:12:13 +0100233 config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL);
Simon Kelleydfa666f2004-08-02 18:27:27 +0100234
235 if (have_config(config, CONFIG_NAME))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000236 hostname = config->hostname;
237 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
238 {
239 int len = option_len(opt);
Simon Kelley3be34542004-09-11 19:12:13 +0100240 hostname = daemon->dhcp_buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000241 memcpy(hostname, option_ptr(opt), len);
242 /* May not be zero terminated */
243 hostname[len] = 0;
244 /* ensure there are no strange chars in there */
245 if (!canonicalise(hostname))
246 hostname = NULL;
Simon Kelley44a2a312004-03-10 20:04:35 +0000247 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000248 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000249 char *dot = strchr(hostname, '.');
250 if (dot)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000251 {
Simon Kelley3be34542004-09-11 19:12:13 +0100252 if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
Simon Kelley44a2a312004-03-10 20:04:35 +0000253 {
254 syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
255 hostname = NULL;
256 }
257 else
Simon Kelleya2226412004-05-13 20:27:08 +0100258 {
259 *dot = 0; /* truncate */
260 if (strlen(hostname) == 0)
261 hostname = NULL; /* nothing left */
262 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000263 }
Simon Kelleydfa666f2004-08-02 18:27:27 +0100264
265 /* Search again now we have a hostname.
266 Only accept configs without CLID and HWADDR here, (they won't match)
267 to avoid impersonation by name. */
268 if (!config)
269 {
Simon Kelley3be34542004-09-11 19:12:13 +0100270 struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname);
Simon Kelleydfa666f2004-08-02 18:27:27 +0100271 if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
272 config = new;
273 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000274 }
275 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100276
Simon Kelleya2226412004-05-13 20:27:08 +0100277 if (have_config(config, CONFIG_NETID))
278 {
279 config->netid.next = netid;
280 netid = &config->netid;
281 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100282
Simon Kelleya2226412004-05-13 20:27:08 +0100283 /* Theres a chance that carefully chosen data could match the same
284 vendor/user option twice and make a loop in the netid chain. */
Simon Kelley3be34542004-09-11 19:12:13 +0100285 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleya2226412004-05-13 20:27:08 +0100286 vendor->used = 0;
287
288 if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
Simon Kelley3be34542004-09-11 19:12:13 +0100289 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleya2226412004-05-13 20:27:08 +0100290 if (vendor->is_vendor && !vendor->used)
291 {
292 int i;
293 for (i = 0; i <= (option_len(opt) - vendor->len); i++)
294 if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
295 {
296 vendor->used = 1;
297 vendor->netid.next = netid;
298 netid = &vendor->netid;
299 break;
300 }
301 }
302
303 if ((opt = option_find(mess, sz, OPTION_USER_CLASS)))
304 {
305 unsigned char *ucp = option_ptr(opt);
306 int j;
307 for (j = 0; j < option_len(opt); j += ucp[j] + 1)
Simon Kelley3be34542004-09-11 19:12:13 +0100308 for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
Simon Kelleya2226412004-05-13 20:27:08 +0100309 if (!vendor->is_vendor && !vendor->used)
310 {
311 int i;
312 for (i = 0; i <= (ucp[j] - vendor->len); i++)
313 if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0)
314 {
315 vendor->used = 1;
316 vendor->netid.next = netid;
317 netid = &vendor->netid;
318 break;
319 }
320 }
321 }
322
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100323 /* Can have setting to ignore the client ID for a particular MAC address or hostname */
324 if (have_config(config, CONFIG_NOCLID))
325 {
326 clid = mess->chaddr;
327 clid_len = 0;
328 }
329
330 /* do we have a lease in store? */
331 lease = lease_find_by_client(clid, clid_len);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000332
Simon Kelley36717ee2004-09-20 19:20:58 +0100333 def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
334
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000335 if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
336 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000337 unsigned int req_time = option_uint(opt, 4);
Simon Kelley36717ee2004-09-20 19:20:58 +0100338
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000339 if (def_time == 0xffffffff ||
340 (req_time != 0xffffffff && req_time < def_time))
341 expires_time = renewal_time = req_time;
342 else
343 expires_time = renewal_time = def_time;
344 }
345 else
346 {
347 renewal_time = def_time;
348 if (lease)
349 expires_time = (unsigned int)difftime(lease->expires, now);
350 else
351 expires_time = def_time;
352 }
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100353
354 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
355 {
356 int len = option_len(opt);
Simon Kelley3be34542004-09-11 19:12:13 +0100357 req_options = daemon->dhcp_buff2;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100358 memcpy(req_options, option_ptr(opt), len);
359 req_options[len] = OPTION_END;
360 }
361
Simon Kelley3be34542004-09-11 19:12:13 +0100362 switch (mess_type)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000363 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000364 case DHCPDECLINE:
365 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
366 (iface_addr.s_addr != option_addr(opt).s_addr))
367 return 0;
368
369 /* sanitise any message. Paranoid? Moi? */
370 if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
371 {
Simon Kelley3be34542004-09-11 19:12:13 +0100372 char *p = option_ptr(opt), *q = daemon->dhcp_buff;
Simon Kelley44a2a312004-03-10 20:04:35 +0000373 int i;
374
375 for (i = option_len(opt); i > 0; i--)
376 {
377 char c = *p++;
378 if (isprint(c))
379 *q++ = c;
380 }
381 *q++ = 0; /* add terminator */
Simon Kelley3be34542004-09-11 19:12:13 +0100382 message = daemon->dhcp_buff;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000383 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000384
385 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
386 return 0;
387
388 log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
389
390 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
391 lease_prune(lease, now);
392
Simon Kelley33820b72004-04-03 21:10:00 +0100393 if (have_config(config, CONFIG_ADDR) &&
Simon Kelley44a2a312004-03-10 20:04:35 +0000394 config->addr.s_addr == option_addr(opt).s_addr)
395 {
396 syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr));
Simon Kelley33820b72004-04-03 21:10:00 +0100397 config->flags &= ~CONFIG_ADDR ;
Simon Kelley44a2a312004-03-10 20:04:35 +0000398 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100399 else
400 /* make sure this host gets a different address next time. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100401 for (; context; context = context->current)
402 context->addr_epoch++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000403
404 return 0;
405
406 case DHCPRELEASE:
407 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
408 (iface_addr.s_addr != option_addr(opt).s_addr))
409 return 0;
410
411 log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL);
412
413 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
414 lease_prune(lease, now);
415
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 return 0;
417
418 case DHCPDISCOVER:
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000419 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100420 addr = option_addr(opt);
Simon Kelley33820b72004-04-03 21:10:00 +0100421 if (have_config(config, CONFIG_DISABLE))
422 message = "ignored";
Simon Kelley9c74ec02004-08-13 21:13:03 +0100423 else if (have_config(config, CONFIG_ADDR) &&
Simon Kelley3be34542004-09-11 19:12:13 +0100424 (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000425 mess->yiaddr = config->addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100426 else if (lease && address_available(context, lease->addr))
Simon Kelley1cff1662004-03-12 08:12:58 +0000427 mess->yiaddr = lease->addr;
Simon Kelleydfa666f2004-08-02 18:27:27 +0100428 else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
Simon Kelley3be34542004-09-11 19:12:13 +0100429 !config_find_by_address(daemon->dhcp_conf, addr))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100430 mess->yiaddr = addr;
Simon Kelley3be34542004-09-11 19:12:13 +0100431 else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100432 message = "no address available";
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100433 log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
434
Simon Kelley33820b72004-04-03 21:10:00 +0100435 if (message)
436 return 0;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100437
Simon Kelley3be34542004-09-11 19:12:13 +0100438 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
439 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000440 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
441 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
442 p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100443 /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
444 if (expires_time != 0xffffffff)
445 {
446 p = option_put(p, end, OPTION_T1, 4, (expires_time/2));
447 p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8));
448 }
Simon Kelley3be34542004-09-11 19:12:13 +0100449 p = do_req_options(context, p, end, req_options, daemon,
450 NULL, iface_addr, netid, subnet_addr);
Simon Kelleya2226412004-05-13 20:27:08 +0100451 p = option_end(p, end, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000452
Simon Kelley44a2a312004-03-10 20:04:35 +0000453 log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000454 return p - (unsigned char *)mess;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000455
456 case DHCPREQUEST:
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100457 if (have_config(config, CONFIG_DISABLE))
458 message = "disabled";
459 else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000460 {
461 /* SELECTING or INIT_REBOOT */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000462 mess->yiaddr = option_addr(opt);
Simon Kelley44a2a312004-03-10 20:04:35 +0000463
Simon Kelley3be34542004-09-11 19:12:13 +0100464 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000465 {
Simon Kelley3be34542004-09-11 19:12:13 +0100466 /* SELECTING */
467 if (iface_addr.s_addr != option_addr(opt).s_addr)
468 return 0;
469
470 /* If a lease exists for this host and another address, squash it. */
471 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
472 {
473 lease_prune(lease, now);
474 lease = NULL;
475 }
476
477 if (!lease)
478 {
479 if (lease_find_by_addr(mess->yiaddr))
480 message = "address in use";
481 else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
482 message = "no leases left";
483 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000484 }
Simon Kelley3be34542004-09-11 19:12:13 +0100485 else
486 {
487 /* INIT-REBOOT */
488 if (!lease)
489 return 0;
490
491 if (lease->addr.s_addr != mess->yiaddr.s_addr)
492 message = "wrong address";
493 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000494 }
495 else
496 {
497 /* RENEWING or REBINDING */
498 /* Must exist a lease for this address */
Simon Kelley44a2a312004-03-10 20:04:35 +0000499 if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
500 message = "lease not found";
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100501
502 /* desynchronise renewals */
503 fuzz = rand16();
504 while (fuzz > (renewal_time/16))
Simon Kelley3be34542004-09-11 19:12:13 +0100505 fuzz = fuzz/2;
506
507 mess->yiaddr = mess->ciaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000508 }
509
Simon Kelleydfa666f2004-08-02 18:27:27 +0100510 if (!message)
511 {
512 struct dhcp_config *addr_config;
513 /* If a machine moves networks whilst it has a lease, we catch that here. */
514 if (!is_same_net(mess->yiaddr, context->start, context->netmask))
515 message = "wrong network";
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100516
Simon Kelleydfa666f2004-08-02 18:27:27 +0100517 /* Check for renewal of a lease which is now outside the allowed range. */
518 else if (!address_available(context, mess->yiaddr) &&
519 (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
520 message = "address no longer available";
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100521
Simon Kelleydfa666f2004-08-02 18:27:27 +0100522 /* Check if a new static address has been configured. Be very sure that
523 when the client does DISCOVER, it will get the static address, otherwise
524 an endless protocol loop will ensue. */
525
526 else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
527 message = "static lease available";
528
529 /* Check to see if the address is reserved as a static address for another host */
Simon Kelley3be34542004-09-11 19:12:13 +0100530 else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
Simon Kelleydfa666f2004-08-02 18:27:27 +0100531 message ="address reserved";
532 }
533
Simon Kelley44a2a312004-03-10 20:04:35 +0000534 log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
535
536 if (message)
537 {
538 log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message);
539
540 mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
541 bootp_option_put(mess, NULL, NULL);
542 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
543 p = option_put_string(p, end, OPTION_MESSAGE, message);
Simon Kelleya2226412004-05-13 20:27:08 +0100544 p = option_end(p, end, mess);
Simon Kelley44a2a312004-03-10 20:04:35 +0000545 mess->flags |= htons(0x8000); /* broadcast */
546 return p - (unsigned char *)mess;
547 }
548
549 log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000550
551 lease_set_hwaddr(lease, mess->chaddr);
Simon Kelleya2226412004-05-13 20:27:08 +0100552 if (hostname)
Simon Kelley3be34542004-09-11 19:12:13 +0100553 lease_set_hostname(lease, hostname, daemon->domain_suffix);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000554 lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
555
Simon Kelley3be34542004-09-11 19:12:13 +0100556 bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
557 mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000558 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
559 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
560 p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
561 if (renewal_time != 0xffffffff)
562 {
Simon Kelley44a2a312004-03-10 20:04:35 +0000563 p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
564 p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
565 }
Simon Kelley3be34542004-09-11 19:12:13 +0100566 p = do_req_options(context, p, end, req_options, daemon,
567 hostname, iface_addr, netid, subnet_addr);
Simon Kelleya2226412004-05-13 20:27:08 +0100568 p = option_end(p, end, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000569 return p - (unsigned char *)mess;
570
571 case DHCPINFORM:
Simon Kelley33820b72004-04-03 21:10:00 +0100572 if (have_config(config, CONFIG_DISABLE))
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100573 message = "ignored";
Simon Kelley33820b72004-04-03 21:10:00 +0100574
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100575 log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message);
576
577 if (message || mess->ciaddr.s_addr == 0)
578 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000579
Simon Kelley44a2a312004-03-10 20:04:35 +0000580 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
581 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
Simon Kelley3be34542004-09-11 19:12:13 +0100582 p = do_req_options(context, p, end, req_options, daemon,
583 hostname, iface_addr, netid, subnet_addr);
Simon Kelleya2226412004-05-13 20:27:08 +0100584 p = option_end(p, end, mess);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000585
Simon Kelley44a2a312004-03-10 20:04:35 +0000586 log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000587 return p - (unsigned char *)mess;
588 }
589
590 return 0;
591}
592
Simon Kelley44a2a312004-03-10 20:04:35 +0000593static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000594{
Simon Kelley3be34542004-09-11 19:12:13 +0100595 syslog(LOG_INFO, "%s%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s",
596 type ? "DHCP" : "BOOTP",
597 type ? type : "",
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000598 interface,
599 addr ? " " : "",
600 addr ? inet_ntoa(*addr) : "",
Simon Kelley44a2a312004-03-10 20:04:35 +0000601 hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5],
602 string ? " " : "",
603 string ? string : "");
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000604}
605
606static int option_len(unsigned char *opt)
607{
608 return opt[1];
609}
610
611static void *option_ptr(unsigned char *opt)
612{
613 return &opt[2];
614}
615
616static struct in_addr option_addr(unsigned char *opt)
617{
618 /* this worries about unaligned data in the option. */
619 /* struct in_addr is network byte order */
620 struct in_addr ret;
621
622 memcpy(&ret, option_ptr(opt), INADDRSZ);
623
624 return ret;
625}
626
Simon Kelley44a2a312004-03-10 20:04:35 +0000627static unsigned int option_uint(unsigned char *opt, int size)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000628{
629 /* this worries about unaligned data and byte order */
Simon Kelley44a2a312004-03-10 20:04:35 +0000630 unsigned int ret = 0;
631 int i;
632 unsigned char *p = option_ptr(opt);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000633
Simon Kelley44a2a312004-03-10 20:04:35 +0000634 for (i = 0; i < size; i++)
635 ret = (ret << 8) | *p++;
636
637 return ret;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000638}
639
640static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
641{
642 memset(mess->sname, 0, sizeof(mess->sname));
643 memset(mess->file, 0, sizeof(mess->file));
644 if (sname)
645 strncpy(mess->sname, sname, sizeof(mess->sname)-1);
646 if (filename)
647 strncpy(mess->file, filename, sizeof(mess->file)-1);
648}
649
650static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
651{
652 int i;
Simon Kelley44a2a312004-03-10 20:04:35 +0000653
654 /* always keep one octet space for the END option. */
Simon Kelleya2226412004-05-13 20:27:08 +0100655 if (p + len + 3 < end)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000656 {
657 *(p++) = opt;
Simon Kelleya2226412004-05-13 20:27:08 +0100658 *(p++) = len;
659
660 for (i = 0; i < len; i++)
661 *(p++) = val >> (8 * (len - (i + 1)));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000662 }
663 return p;
664}
665
Simon Kelleya2226412004-05-13 20:27:08 +0100666static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start)
667{
668 *(p++) = OPTION_END;
669 while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
670 *p++ = 0;
671
672 return p;
673}
674
Simon Kelley44a2a312004-03-10 20:04:35 +0000675static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
676{
Simon Kelley3be34542004-09-11 19:12:13 +0100677 int len = strlen(string);
678
679 if (p + len + 3 < end)
Simon Kelley44a2a312004-03-10 20:04:35 +0000680 {
681 *(p++) = opt;
Simon Kelley3be34542004-09-11 19:12:13 +0100682 *(p++) = len;
683 memcpy(p, string, len);
684 p += len;
Simon Kelley44a2a312004-03-10 20:04:35 +0000685 }
Simon Kelley3be34542004-09-11 19:12:13 +0100686
Simon Kelley44a2a312004-03-10 20:04:35 +0000687 return p;
688}
689
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000690static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
691{
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000692 if (!p)
693 return NULL;
694
695 while (*p != OPTION_END)
696 {
697 if (end && (p >= end))
698 return 0; /* malformed packet */
699 else if (*p == OPTION_PAD)
700 p++;
701 else if (*p == OPTION_OVERLOAD)
702 {
703 if (end && (p >= end - 3))
704 return 0; /* malformed packet */
705 if (overload)
706 *overload = *(p+2);
707 p += 3;
708 }
709 else
710 {
711 int opt_len;;
712 if (end && (p >= end - 2))
713 return 0; /* malformed packet */
714 opt_len = option_len(p);
715 if (end && (p >= end - (2 + opt_len)))
716 return 0; /* malformed packet */
717 if (*p == opt)
718 return p;
719 p += opt_len + 2;
720 }
721 }
722
723 return NULL;
724}
725
726static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
727{
728 int overload = 0;
729 unsigned char *ret;
730
Simon Kelley3be34542004-09-11 19:12:13 +0100731 /* skip over DHCP cookie; */
732 ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, &overload);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000733
734 if (!ret && (overload & 1))
735 ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload);
736
737 if (!ret && (overload & 2))
738 ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
739
740 return ret;
741}
742
743static int in_list(unsigned char *list, int opt)
744{
745 int i;
746
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100747 /* If no requested options, send everything, not nothing. */
748 if (!list)
749 return 1;
750
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000751 for (i = 0; list[i] != OPTION_END; i++)
752 if (opt == list[i])
753 return 1;
754
755 return 0;
756}
757
Simon Kelleya2226412004-05-13 20:27:08 +0100758static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000759{
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100760 struct dhcp_opt *tmp;
Simon Kelleya2226412004-05-13 20:27:08 +0100761 struct dhcp_netid *tmp1;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100762
763 for (tmp = opts; tmp; tmp = tmp->next)
Simon Kelleya2226412004-05-13 20:27:08 +0100764 if (tmp->opt == opt)
765 {
766 if (netid)
767 {
768 if (tmp->netid)
769 for (tmp1 = netid; tmp1; tmp1 = tmp1->next)
770 if (strcmp(tmp->netid, tmp1->net) == 0)
771 return tmp;
772 }
773 else if (!tmp->netid)
774 return tmp;
775 }
776
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100777 return netid ? option_find2(NULL, opts, opt) : NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000778}
779
780static unsigned char *do_req_options(struct dhcp_context *context,
781 unsigned char *p, unsigned char *end,
782 unsigned char *req_options,
Simon Kelley3be34542004-09-11 19:12:13 +0100783 struct daemon *daemon,
784 char *hostname,
Simon Kelley44a2a312004-03-10 20:04:35 +0000785 struct in_addr iface_addr,
Simon Kelley3be34542004-09-11 19:12:13 +0100786 struct dhcp_netid *netid,
787 struct in_addr subnet_addr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000788{
Simon Kelley3be34542004-09-11 19:12:13 +0100789 struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
790
Simon Kelley1ab84e22004-01-29 16:48:35 +0000791 if (in_list(req_options, OPTION_MAXMESSAGE))
Simon Kelley3be34542004-09-11 19:12:13 +0100792 p = option_put(p, end, OPTION_MAXMESSAGE, 2, end - (unsigned char *)daemon->dhcp_packet);
Simon Kelley1ab84e22004-01-29 16:48:35 +0000793
Simon Kelley3be34542004-09-11 19:12:13 +0100794 /* rfc3011 says this doesn't need to be in the requested options list. */
795 if (subnet_addr.s_addr)
796 p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
797
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000798 if (in_list(req_options, OPTION_NETMASK) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100799 !option_find2(netid, config_opts, OPTION_NETMASK))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000800 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
801
Simon Kelley3be34542004-09-11 19:12:13 +0100802 /* May not have a "guessed" broadcast address if we got no packets via a relay
803 from this net yet (ie just unicast renewals after a restart */
804 if (context->broadcast.s_addr &&
805 in_list(req_options, OPTION_BROADCAST) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100806 !option_find2(netid, config_opts, OPTION_BROADCAST))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000807 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
808
Simon Kelley3be34542004-09-11 19:12:13 +0100809 /* Same comments as broadcast apply, and also may not be able to get a sensible
810 default when using subnet select. User must configure by steam in that case. */
811 if (context->router.s_addr &&
812 in_list(req_options, OPTION_ROUTER) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100813 !option_find2(netid, config_opts, OPTION_ROUTER))
Simon Kelley3be34542004-09-11 19:12:13 +0100814 p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000815
816 if (in_list(req_options, OPTION_DNSSERVER) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100817 !option_find2(netid, config_opts, OPTION_DNSSERVER))
Simon Kelley44a2a312004-03-10 20:04:35 +0000818 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000819
Simon Kelley3be34542004-09-11 19:12:13 +0100820 if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
Simon Kelley33820b72004-04-03 21:10:00 +0100821 !option_find2(netid, config_opts, OPTION_DOMAINNAME))
Simon Kelley3be34542004-09-11 19:12:13 +0100822 p = option_put_string(p, end, OPTION_DOMAINNAME, daemon->domain_suffix);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000823
824 /* Note that we ignore attempts to set the hostname using
825 --dhcp-option=12,<name> */
Simon Kelley44a2a312004-03-10 20:04:35 +0000826 if (hostname && in_list(req_options, OPTION_HOSTNAME))
827 p = option_put_string(p, end, OPTION_HOSTNAME, hostname);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000828
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100829 for (opt=config_opts; opt; opt = opt->next)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000830 {
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100831 if (opt->opt == OPTION_HOSTNAME ||
832 opt->opt == OPTION_MAXMESSAGE ||
833 !in_list(req_options, opt->opt) ||
834 opt != option_find2(netid, config_opts, opt->opt) ||
835 p + opt->len + 3 >= end)
Simon Kelley33820b72004-04-03 21:10:00 +0100836 continue;
837
838 /* For the options we have default values on
839 dhc-option=<optionno> means "don't include this option"
840 not "include a zero-length option" */
841 if (opt->len == 0 &&
842 (opt->opt == OPTION_NETMASK ||
843 opt->opt == OPTION_BROADCAST ||
844 opt->opt == OPTION_ROUTER ||
845 opt->opt == OPTION_DNSSERVER))
846 continue;
847
848 *(p++) = opt->opt;
849 *(p++) = opt->len;
850 if (opt->len == 0)
851 continue;
Simon Kelleya84fa1d2004-04-23 22:21:21 +0100852
Simon Kelley33820b72004-04-03 21:10:00 +0100853 if (opt->is_addr)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000854 {
Simon Kelley33820b72004-04-03 21:10:00 +0100855 int j;
856 struct in_addr *a = (struct in_addr *)opt->val;
857 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
Simon Kelley1ab84e22004-01-29 16:48:35 +0000858 {
Simon Kelley33820b72004-04-03 21:10:00 +0100859 /* zero means "self" */
860 if (a->s_addr == 0)
861 memcpy(p, &iface_addr, INADDRSZ);
Simon Kelley1ab84e22004-01-29 16:48:35 +0000862 else
Simon Kelley33820b72004-04-03 21:10:00 +0100863 memcpy(p, a, INADDRSZ);
864 p += INADDRSZ;
Simon Kelley1ab84e22004-01-29 16:48:35 +0000865 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000866 }
Simon Kelley33820b72004-04-03 21:10:00 +0100867 else
868 {
869 memcpy(p, opt->val, opt->len);
870 p += opt->len;
871 }
872 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000873 return p;
874}
875
876