]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/rfc2131.c
import of dnsmasq-2.3.tar.gz
[people/ms/dnsmasq.git] / src / rfc2131.c
1 /* 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
21 #define OPTION_PAD 0
22 #define OPTION_NETMASK 1
23 #define OPTION_ROUTER 3
24 #define OPTION_DNSSERVER 6
25 #define OPTION_HOSTNAME 12
26 #define OPTION_DOMAINNAME 15
27 #define OPTION_BROADCAST 28
28 #define OPTION_REQUESTED_IP 50
29 #define OPTION_LEASE_TIME 51
30 #define OPTION_OVERLOAD 52
31 #define OPTION_MESSAGE_TYPE 53
32 #define OPTION_SERVER_IDENTIFIER 54
33 #define OPTION_REQUESTED_OPTIONS 55
34 #define OPTION_MESSAGE 56
35 #define OPTION_MAXMESSAGE 57
36 #define OPTION_T1 58
37 #define OPTION_T2 59
38 #define OPTION_CLIENT_ID 61
39 #define OPTION_END 255
40
41 #define DHCPDISCOVER 1
42 #define DHCPOFFER 2
43 #define DHCPREQUEST 3
44 #define DHCPDECLINE 4
45 #define DHCPACK 5
46 #define DHCPNAK 6
47 #define DHCPRELEASE 7
48 #define DHCPINFORM 8
49
50 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
51 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
52 static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
53 static int option_len(unsigned char *opt);
54 static void *option_ptr(unsigned char *opt);
55 static struct in_addr option_addr(unsigned char *opt);
56 static unsigned int option_uint(unsigned char *opt, int size);
57 static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
58 static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
59 static unsigned char *do_req_options(struct dhcp_context *context,
60 unsigned char *p, unsigned char *end,
61 unsigned char *req_options,
62 struct dhcp_opt *config_opts,
63 char *domainname, char *hostname,
64 struct in_addr relay,
65 struct in_addr iface_addr,
66 int iface_mtu);
67
68
69 int dhcp_reply(struct dhcp_context *context,
70 struct in_addr iface_addr,
71 char *iface_name,
72 int iface_mtu,
73 struct udp_dhcp_packet *rawpacket,
74 unsigned int sz, time_t now, char *namebuff,
75 struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
76 char *domain_suffix, char *dhcp_file, char *dhcp_sname,
77 struct in_addr dhcp_next_server)
78 {
79 unsigned char *opt, *clid;
80 struct dhcp_lease *lease;
81 int clid_len;
82 struct dhcp_packet *mess = &rawpacket->data;
83 unsigned char *p = mess->options;
84 /* default max reply packet length, max be overridden */
85 unsigned char *end = (unsigned char *)(rawpacket + 1);
86 char *hostname = NULL;
87 char *req_options = NULL;
88 char *message = NULL;
89 unsigned int renewal_time, expires_time, def_time;
90 struct dhcp_config *config;
91
92 if (mess->op != BOOTREQUEST ||
93 mess->htype != ARPHRD_ETHER ||
94 mess->hlen != ETHER_ADDR_LEN ||
95 mess->cookie != htonl(DHCP_COOKIE))
96 return 0;
97
98 mess->op = BOOTREPLY;
99
100 if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE)))
101 {
102 int maxsize = (int)option_uint(opt, 2);
103 if (maxsize > DNSMASQ_PACKETSZ)
104 maxsize = DNSMASQ_PACKETSZ;
105 if (maxsize > iface_mtu)
106 maxsize = iface_mtu;
107
108 end = ((unsigned char *)rawpacket) + maxsize;
109 }
110
111 /* If there is no client identifier option, use the hardware address */
112 if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
113 {
114 clid = option_ptr(opt);
115 clid_len = option_len(opt);
116 }
117 else
118 {
119 clid = mess->chaddr;
120 clid_len = 0;
121 }
122
123 /* do we have a lease in store? */
124 lease = lease_find_by_client(clid, clid_len);
125
126 if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
127 {
128 int len = option_len(opt);
129 req_options = namebuff;
130 memcpy(req_options, option_ptr(opt), len);
131 req_options[len] = OPTION_END;
132 }
133
134 if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
135 config->hostname)
136 hostname = config->hostname;
137 else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
138 {
139 int len = option_len(opt);
140 /* namebuff is 1K long, use half for requested options and half for hostname */
141 /* len < 256 by definition */
142 hostname = namebuff + 500;
143 memcpy(hostname, option_ptr(opt), len);
144 /* May not be zero terminated */
145 hostname[len] = 0;
146 /* ensure there are no strange chars in there */
147 if (!canonicalise(hostname))
148 hostname = NULL;
149 else
150 {
151 char *dot = strchr(hostname, '.');
152 if (dot)
153 {
154 if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix))
155 {
156 syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
157 hostname = NULL;
158 }
159 else
160 *dot = 0; /* truncate */
161 }
162 }
163 }
164
165 /* search again now we have a hostname */
166 config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
167 def_time = config && config->lease_time ? config->lease_time : context->lease_time;
168
169 if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
170 {
171 unsigned int req_time = option_uint(opt, 4);
172
173 if (def_time == 0xffffffff ||
174 (req_time != 0xffffffff && req_time < def_time))
175 expires_time = renewal_time = req_time;
176 else
177 expires_time = renewal_time = def_time;
178 }
179 else
180 {
181 renewal_time = def_time;
182 if (lease)
183 expires_time = (unsigned int)difftime(lease->expires, now);
184 else
185 expires_time = def_time;
186 }
187
188 if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
189 return 0;
190
191 switch (opt[2])
192 {
193 case DHCPDECLINE:
194 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
195 (iface_addr.s_addr != option_addr(opt).s_addr))
196 return 0;
197
198 /* sanitise any message. Paranoid? Moi? */
199 if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
200 {
201 char *p = option_ptr(opt), *q = namebuff;
202 int i;
203
204 for (i = option_len(opt); i > 0; i--)
205 {
206 char c = *p++;
207 if (isprint(c))
208 *q++ = c;
209 }
210 *q++ = 0; /* add terminator */
211 message = namebuff;
212 }
213
214 if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
215 return 0;
216
217 log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
218
219 if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
220 lease_prune(lease, now);
221
222 if (config && config->addr.s_addr &&
223 config->addr.s_addr == option_addr(opt).s_addr)
224 {
225 syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr));
226 config->addr.s_addr = 0;
227 }
228
229 return 0;
230
231 case DHCPRELEASE:
232 if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
233 (iface_addr.s_addr != option_addr(opt).s_addr))
234 return 0;
235
236 log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL);
237
238 if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
239 lease_prune(lease, now);
240
241 return 0;
242
243 case DHCPDISCOVER:
244 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
245 mess->yiaddr = option_addr(opt);
246
247 log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, NULL);
248
249 if (lease &&
250 ((lease->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr)))
251 mess->yiaddr = lease->addr;
252 else if (config && config->addr.s_addr && !lease_find_by_addr(config->addr))
253 mess->yiaddr = config->addr;
254 else if ((!opt || !address_available(context, mess->yiaddr)) &&
255 !address_allocate(context, dhcp_configs, &mess->yiaddr))
256 {
257 syslog(LOG_WARNING, "address pool exhausted");
258 return 0;
259 }
260
261 bootp_option_put(mess, dhcp_file, dhcp_sname);
262 mess->siaddr = dhcp_next_server;
263 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
264 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
265 p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
266 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
267 NULL, mess->giaddr, iface_addr, iface_mtu);
268 p = option_put(p, end, OPTION_END, 0, 0);
269
270 log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
271 return p - (unsigned char *)mess;
272
273
274 case DHCPREQUEST:
275 if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
276 {
277 /* SELECTING or INIT_REBOOT */
278 mess->yiaddr = option_addr(opt);
279 /* The RFC says that this is already zero, but there exist
280 real-world counter examples. */
281 mess->ciaddr.s_addr = 0;
282
283 if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) &&
284 (iface_addr.s_addr != option_addr(opt).s_addr))
285 return 0;
286
287 /* If a lease exists for this host and another address, squash it. */
288 if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
289 {
290 lease_prune(lease, now);
291 lease = NULL;
292 }
293
294 /* accept addresses in the dynamic range or ones allocated statically to
295 particular hosts or an address which the host already has. */
296 if (!lease)
297 {
298 if (!address_available(context, mess->yiaddr) &&
299 (!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr))
300 message = "address unavailable";
301 else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
302 message = "no leases left";
303 }
304 }
305 else
306 {
307 /* RENEWING or REBINDING */
308 /* Must exist a lease for this address */
309 if (!mess->ciaddr.s_addr)
310 return 0;
311
312 mess->yiaddr = mess->ciaddr;
313 if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
314 message = "lease not found";
315 }
316
317 /* If a machine moves networks whilst it has a lease, we catch that here. */
318 if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr))
319 message = "wrong network";
320
321 log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
322
323 if (message)
324 {
325 log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message);
326
327 mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
328 bootp_option_put(mess, NULL, NULL);
329 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
330 p = option_put_string(p, end, OPTION_MESSAGE, message);
331 p = option_put(p, end, OPTION_END, 0, 0);
332 mess->flags |= htons(0x8000); /* broadcast */
333 return p - (unsigned char *)mess;
334 }
335
336 log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
337
338 lease_set_hwaddr(lease, mess->chaddr);
339 lease_set_hostname(lease, hostname, domain_suffix);
340 lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
341
342 bootp_option_put(mess, dhcp_file, dhcp_sname);
343 mess->siaddr = dhcp_next_server;
344 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
345 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
346 p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
347 if (renewal_time != 0xffffffff)
348 {
349 unsigned short fuzz = rand16();
350 while (fuzz > (renewal_time/16))
351 fuzz = fuzz/2;
352 p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
353 p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
354 }
355 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
356 hostname, mess->giaddr, iface_addr, iface_mtu);
357 p = option_put(p, end, OPTION_END, 0, 0);
358 return p - (unsigned char *)mess;
359
360 case DHCPINFORM:
361 log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
362
363 p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
364 p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
365 p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
366 hostname, mess->giaddr, iface_addr, iface_mtu);
367 p = option_put(p, end, OPTION_END, 0, 0);
368
369 log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
370 return p - (unsigned char *)mess;
371 }
372
373 return 0;
374 }
375
376 static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string)
377 {
378 syslog(LOG_INFO, "DHCP%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s",
379 type,
380 interface,
381 addr ? " " : "",
382 addr ? inet_ntoa(*addr) : "",
383 hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5],
384 string ? " " : "",
385 string ? string : "");
386 }
387
388 static int option_len(unsigned char *opt)
389 {
390 return opt[1];
391 }
392
393 static void *option_ptr(unsigned char *opt)
394 {
395 return &opt[2];
396 }
397
398 static struct in_addr option_addr(unsigned char *opt)
399 {
400 /* this worries about unaligned data in the option. */
401 /* struct in_addr is network byte order */
402 struct in_addr ret;
403
404 memcpy(&ret, option_ptr(opt), INADDRSZ);
405
406 return ret;
407 }
408
409 static unsigned int option_uint(unsigned char *opt, int size)
410 {
411 /* this worries about unaligned data and byte order */
412 unsigned int ret = 0;
413 int i;
414 unsigned char *p = option_ptr(opt);
415
416 for (i = 0; i < size; i++)
417 ret = (ret << 8) | *p++;
418
419 return ret;
420 }
421
422 static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
423 {
424 memset(mess->sname, 0, sizeof(mess->sname));
425 memset(mess->file, 0, sizeof(mess->file));
426 if (sname)
427 strncpy(mess->sname, sname, sizeof(mess->sname)-1);
428 if (filename)
429 strncpy(mess->file, filename, sizeof(mess->file)-1);
430 }
431
432 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
433 {
434 int i;
435
436 /* always keep one octet space for the END option. */
437 if ((opt == OPTION_END) || (p + len + 3 < end))
438 {
439 *(p++) = opt;
440 if (opt != OPTION_END)
441 {
442 *(p++) = len;
443
444 for (i = 0; i < len; i++)
445 *(p++) = val >> (8 * (len - (i + 1)));
446 }
447 }
448 return p;
449 }
450
451 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
452 {
453 if (p + strlen(string) + 3 < end)
454 {
455 *(p++) = opt;
456 *(p++) = strlen(string);
457 memcpy(p, string, strlen(string));
458 p += strlen(string);
459 }
460 return p;
461 }
462
463 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
464 {
465 if (!p)
466 return NULL;
467
468 while (*p != OPTION_END)
469 {
470 if (end && (p >= end))
471 return 0; /* malformed packet */
472 else if (*p == OPTION_PAD)
473 p++;
474 else if (*p == OPTION_OVERLOAD)
475 {
476 if (end && (p >= end - 3))
477 return 0; /* malformed packet */
478 if (overload)
479 *overload = *(p+2);
480 p += 3;
481 }
482 else
483 {
484 int opt_len;;
485 if (end && (p >= end - 2))
486 return 0; /* malformed packet */
487 opt_len = option_len(p);
488 if (end && (p >= end - (2 + opt_len)))
489 return 0; /* malformed packet */
490 if (*p == opt)
491 return p;
492 p += opt_len + 2;
493 }
494 }
495
496 return NULL;
497 }
498
499 static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
500 {
501 int overload = 0;
502 unsigned char *ret;
503
504 ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, &overload);
505
506 if (!ret && (overload & 1))
507 ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload);
508
509 if (!ret && (overload & 2))
510 ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
511
512 return ret;
513 }
514
515 static int in_list(unsigned char *list, int opt)
516 {
517 int i;
518
519 for (i = 0; list[i] != OPTION_END; i++)
520 if (opt == list[i])
521 return 1;
522
523 return 0;
524 }
525
526 static struct dhcp_opt *option_find2(struct dhcp_context *context, struct dhcp_opt *opts, int opt)
527 {
528 for (; opts; opts = opts->next)
529 if (opts->opt == opt &&
530 (!opts->netid || (context->netid && strcmp(opts->netid, context->netid) == 0)))
531 return opts;
532 return NULL;
533 }
534
535 static unsigned char *do_req_options(struct dhcp_context *context,
536 unsigned char *p, unsigned char *end,
537 unsigned char *req_options,
538 struct dhcp_opt *config_opts,
539 char *domainname, char *hostname,
540 struct in_addr relay,
541 struct in_addr iface_addr,
542 int iface_mtu)
543 {
544 int i;
545
546 if (!req_options)
547 return p;
548
549 if (in_list(req_options, OPTION_MAXMESSAGE))
550 p = option_put(p, end, OPTION_MAXMESSAGE, 2,
551 DNSMASQ_PACKETSZ > iface_mtu ?
552 iface_mtu : DNSMASQ_PACKETSZ);
553
554 if (in_list(req_options, OPTION_NETMASK) &&
555 !option_find2(context, config_opts, OPTION_NETMASK))
556 p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
557
558 if (in_list(req_options, OPTION_BROADCAST) &&
559 !option_find2(context, config_opts, OPTION_BROADCAST))
560 p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
561
562 if (in_list(req_options, OPTION_ROUTER) &&
563 !option_find2(context, config_opts, OPTION_ROUTER))
564 p = option_put(p, end, OPTION_ROUTER, INADDRSZ,
565 ntohl(relay.s_addr ? relay.s_addr : iface_addr.s_addr ));
566
567 if (in_list(req_options, OPTION_DNSSERVER) &&
568 !option_find2(context, config_opts, OPTION_DNSSERVER))
569 p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
570
571 if (domainname && in_list(req_options, OPTION_DOMAINNAME) &&
572 !option_find2(context, config_opts, OPTION_DOMAINNAME))
573 p = option_put_string(p, end, OPTION_DOMAINNAME, domainname);
574
575 /* Note that we ignore attempts to set the hostname using
576 --dhcp-option=12,<name> */
577 if (hostname && in_list(req_options, OPTION_HOSTNAME))
578 p = option_put_string(p, end, OPTION_HOSTNAME, hostname);
579
580 for (i = 0; req_options[i] != OPTION_END; i++)
581 {
582 struct dhcp_opt *opt = option_find2(context, config_opts, req_options[i]);
583 if (req_options[i] != OPTION_HOSTNAME &&
584 req_options[i] != OPTION_MAXMESSAGE &&
585 opt && (p + opt->len + 3 < end))
586 {
587 *(p++) = opt->opt;
588 *(p++) = opt->len;
589 if (opt->len != 0)
590 {
591 if (opt->is_addr)
592 {
593 int j;
594 struct in_addr *a = (struct in_addr *)opt->val;
595 for (j = 0; j < opt->len; j+=INADDRSZ, a++)
596 {
597 /* zero means "self" */
598 if (a->s_addr == 0)
599 memcpy(p, &iface_addr, INADDRSZ);
600 else
601 memcpy(p, a, INADDRSZ);
602 p += INADDRSZ;
603 }
604 }
605 else
606 {
607 memcpy(p, opt->val, opt->len);
608 p += opt->len;
609 }
610 }
611 }
612 }
613
614 return p;
615 }
616
617