]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/rfc2131.c
1 /* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
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.
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.
13 /* Author's email: simon@thekelleys.org.uk */
19 #define DHCP_COOKIE 0x63825363
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
38 #define OPTION_CLIENT_ID 61
39 #define OPTION_END 255
41 #define DHCPDISCOVER 1
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 router
,
65 struct in_addr iface_addr
,
66 int iface_mtu
, char *netid
);
68 static int have_config(struct dhcp_config
*config
, unsigned int mask
)
70 return config
&& (config
->flags
& mask
);
73 int dhcp_reply(struct dhcp_context
*context
,
74 struct in_addr iface_addr
,
77 struct udp_dhcp_packet
*rawpacket
,
78 unsigned int sz
, time_t now
, char *namebuff
,
79 struct dhcp_opt
*dhcp_opts
, struct dhcp_config
*dhcp_configs
,
80 char *domain_suffix
, char *dhcp_file
, char *dhcp_sname
,
81 struct in_addr dhcp_next_server
, struct in_addr router
)
83 unsigned char *opt
, *clid
;
84 struct dhcp_lease
*lease
;
86 struct dhcp_packet
*mess
= &rawpacket
->data
;
87 unsigned char *p
= mess
->options
;
88 /* default max reply packet length, max be overridden */
89 unsigned char *end
= (unsigned char *)(rawpacket
+ 1);
90 char *hostname
= NULL
;
91 char *req_options
= NULL
;
93 unsigned int renewal_time
, expires_time
, def_time
;
94 struct dhcp_config
*config
;
97 if (mess
->op
!= BOOTREQUEST
||
98 mess
->hlen
!= ETHER_ADDR_LEN
||
99 mess
->cookie
!= htonl(DHCP_COOKIE
))
102 /* Token ring is supported when we have packet sockets
103 to make the HW headers for us. We don't have the code to build
104 token ring headers when using BPF. We rely on the fact that
105 token ring hwaddrs are the same size as ethernet hwaddrs. */
108 if (mess
->htype
!= ARPHRD_ETHER
)
111 if (mess
->htype
!= ARPHRD_ETHER
&&
112 mess
->htype
!= ARPHRD_IEEE802
)
116 mess
->op
= BOOTREPLY
;
118 if ((opt
= option_find(mess
, sz
, OPTION_MAXMESSAGE
)))
120 int maxsize
= (int)option_uint(opt
, 2);
121 if (maxsize
> DNSMASQ_PACKETSZ
)
122 maxsize
= DNSMASQ_PACKETSZ
;
123 if (maxsize
> iface_mtu
)
126 end
= ((unsigned char *)rawpacket
) + maxsize
;
129 /* If there is no client identifier option, use the hardware address */
130 if ((opt
= option_find(mess
, sz
, OPTION_CLIENT_ID
)))
132 clid
= option_ptr(opt
);
133 clid_len
= option_len(opt
);
141 /* do we have a lease in store? */
142 lease
= lease_find_by_client(clid
, clid_len
);
144 if ((opt
= option_find(mess
, sz
, OPTION_REQUESTED_OPTIONS
)))
146 int len
= option_len(opt
);
147 req_options
= namebuff
;
148 memcpy(req_options
, option_ptr(opt
), len
);
149 req_options
[len
] = OPTION_END
;
152 if ((config
= find_config(dhcp_configs
, context
, clid
, clid_len
, mess
->chaddr
, NULL
)) &&
153 have_config(config
, CONFIG_NAME
))
154 hostname
= config
->hostname
;
155 else if ((opt
= option_find(mess
, sz
, OPTION_HOSTNAME
)))
157 int len
= option_len(opt
);
158 /* namebuff is 1K long, use half for requested options and half for hostname */
159 /* len < 256 by definition */
160 hostname
= namebuff
+ 500;
161 memcpy(hostname
, option_ptr(opt
), len
);
162 /* May not be zero terminated */
164 /* ensure there are no strange chars in there */
165 if (!canonicalise(hostname
))
169 char *dot
= strchr(hostname
, '.');
172 if (!domain_suffix
|| !hostname_isequal(dot
+1, domain_suffix
))
174 syslog(LOG_WARNING
, "Ignoring DHCP host name %s because it has an illegal domain part", hostname
);
178 *dot
= 0; /* truncate */
183 /* search again now we have a hostname */
184 config
= find_config(dhcp_configs
, context
, clid
, clid_len
, mess
->chaddr
, hostname
);
185 def_time
= have_config(config
, CONFIG_TIME
) ? config
->lease_time
: context
->lease_time
;
186 netid
= have_config(config
, CONFIG_NETID
) ? config
->netid
: context
->netid
;
188 if ((opt
= option_find(mess
, sz
, OPTION_LEASE_TIME
)))
190 unsigned int req_time
= option_uint(opt
, 4);
192 if (def_time
== 0xffffffff ||
193 (req_time
!= 0xffffffff && req_time
< def_time
))
194 expires_time
= renewal_time
= req_time
;
196 expires_time
= renewal_time
= def_time
;
200 renewal_time
= def_time
;
202 expires_time
= (unsigned int)difftime(lease
->expires
, now
);
204 expires_time
= def_time
;
207 if (!(opt
= option_find(mess
, sz
, OPTION_MESSAGE_TYPE
)))
213 if (!(opt
= option_find(mess
, sz
, OPTION_SERVER_IDENTIFIER
)) ||
214 (iface_addr
.s_addr
!= option_addr(opt
).s_addr
))
217 /* sanitise any message. Paranoid? Moi? */
218 if ((opt
= option_find(mess
, sz
, OPTION_MESSAGE
)))
220 char *p
= option_ptr(opt
), *q
= namebuff
;
223 for (i
= option_len(opt
); i
> 0; i
--)
229 *q
++ = 0; /* add terminator */
233 if (!(opt
= option_find(mess
, sz
, OPTION_REQUESTED_IP
)))
236 log_packet("DECLINE", option_ptr(opt
), mess
->chaddr
, iface_name
, message
);
238 if (lease
&& lease
->addr
.s_addr
== option_addr(opt
).s_addr
)
239 lease_prune(lease
, now
);
241 if (have_config(config
, CONFIG_ADDR
) &&
242 config
->addr
.s_addr
== option_addr(opt
).s_addr
)
244 syslog(LOG_WARNING
, "disabling DHCP static address %s", inet_ntoa(config
->addr
));
245 config
->flags
&= ~CONFIG_ADDR
;
251 if (!(opt
= option_find(mess
, sz
, OPTION_SERVER_IDENTIFIER
)) ||
252 (iface_addr
.s_addr
!= option_addr(opt
).s_addr
))
255 log_packet("RELEASE", &mess
->ciaddr
, mess
->chaddr
, iface_name
, NULL
);
257 if (lease
&& lease
->addr
.s_addr
== mess
->ciaddr
.s_addr
)
258 lease_prune(lease
, now
);
263 if ((opt
= option_find(mess
, sz
, OPTION_REQUESTED_IP
)))
264 mess
->yiaddr
= option_addr(opt
);
266 if (have_config(config
, CONFIG_DISABLE
))
268 else if (have_config(config
, CONFIG_ADDR
) && !lease_find_by_addr(config
->addr
))
269 mess
->yiaddr
= config
->addr
;
271 ((lease
->addr
.s_addr
& context
->netmask
.s_addr
) ==
272 (context
->start
.s_addr
& context
->netmask
.s_addr
)))
273 mess
->yiaddr
= lease
->addr
;
274 else if ((!opt
|| !address_available(context
, mess
->yiaddr
)) &&
275 !address_allocate(context
, dhcp_configs
, &mess
->yiaddr
))
276 message
= "no address available";
278 log_packet("DISCOVER", opt
? &mess
->yiaddr
: NULL
, mess
->chaddr
, iface_name
, message
);
282 bootp_option_put(mess
, dhcp_file
, dhcp_sname
);
283 mess
->siaddr
= dhcp_next_server
;
284 p
= option_put(p
, end
, OPTION_MESSAGE_TYPE
, 1, DHCPOFFER
);
285 p
= option_put(p
, end
, OPTION_SERVER_IDENTIFIER
, INADDRSZ
, ntohl(iface_addr
.s_addr
));
286 p
= option_put(p
, end
, OPTION_LEASE_TIME
, 4, expires_time
);
287 p
= do_req_options(context
, p
, end
, req_options
, dhcp_opts
, domain_suffix
,
288 NULL
, router
, iface_addr
, iface_mtu
, netid
);
289 p
= option_put(p
, end
, OPTION_END
, 0, 0);
291 log_packet("OFFER" , &mess
->yiaddr
, mess
->chaddr
, iface_name
, NULL
);
292 return p
- (unsigned char *)mess
;
296 if ((opt
= option_find(mess
, sz
, OPTION_REQUESTED_IP
)))
298 /* SELECTING or INIT_REBOOT */
299 mess
->yiaddr
= option_addr(opt
);
300 /* The RFC says that this is already zero, but there exist
301 real-world counter examples. */
302 mess
->ciaddr
.s_addr
= 0;
304 if ((opt
= option_find(mess
, sz
, OPTION_SERVER_IDENTIFIER
)) &&
305 (iface_addr
.s_addr
!= option_addr(opt
).s_addr
))
308 /* If a lease exists for this host and another address, squash it. */
309 if (lease
&& lease
->addr
.s_addr
!= mess
->yiaddr
.s_addr
)
311 lease_prune(lease
, now
);
315 /* accept addresses in the dynamic range or ones allocated statically to
316 particular hosts or an address which the host already has. */
319 if (!address_available(context
, mess
->yiaddr
) &&
320 (!have_config(config
, CONFIG_ADDR
) || config
->addr
.s_addr
!= mess
->yiaddr
.s_addr
))
321 message
= "address unavailable";
322 else if (!(lease
= lease_allocate(clid
, clid_len
, mess
->yiaddr
)))
323 message
= "no leases left";
328 /* RENEWING or REBINDING */
329 /* Must exist a lease for this address */
330 if (!mess
->ciaddr
.s_addr
)
333 mess
->yiaddr
= mess
->ciaddr
;
334 if (!lease
|| mess
->ciaddr
.s_addr
!= lease
->addr
.s_addr
)
335 message
= "lease not found";
338 /* If a machine moves networks whilst it has a lease, we catch that here. */
339 if ((mess
->yiaddr
.s_addr
& context
->netmask
.s_addr
) != (context
->start
.s_addr
& context
->netmask
.s_addr
))
340 message
= "wrong network";
342 if (have_config(config
, CONFIG_DISABLE
))
343 message
= "disabled";
345 log_packet("REQUEST", &mess
->yiaddr
, mess
->chaddr
, iface_name
, NULL
);
349 log_packet("NAK", &mess
->yiaddr
, mess
->chaddr
, iface_name
, message
);
351 mess
->siaddr
.s_addr
= mess
->yiaddr
.s_addr
= mess
->ciaddr
.s_addr
= 0;
352 bootp_option_put(mess
, NULL
, NULL
);
353 p
= option_put(p
, end
, OPTION_MESSAGE_TYPE
, 1, DHCPNAK
);
354 p
= option_put_string(p
, end
, OPTION_MESSAGE
, message
);
355 p
= option_put(p
, end
, OPTION_END
, 0, 0);
356 mess
->flags
|= htons(0x8000); /* broadcast */
357 return p
- (unsigned char *)mess
;
360 log_packet("ACK", &mess
->yiaddr
, mess
->chaddr
, iface_name
, hostname
);
362 lease_set_hwaddr(lease
, mess
->chaddr
);
363 lease_set_hostname(lease
, hostname
, domain_suffix
);
364 lease_set_expires(lease
, renewal_time
== 0xffffffff ? 0 : now
+ (time_t)renewal_time
);
366 bootp_option_put(mess
, dhcp_file
, dhcp_sname
);
367 mess
->siaddr
= dhcp_next_server
;
368 p
= option_put(p
, end
, OPTION_MESSAGE_TYPE
, 1, DHCPACK
);
369 p
= option_put(p
, end
, OPTION_SERVER_IDENTIFIER
, INADDRSZ
, ntohl(iface_addr
.s_addr
));
370 p
= option_put(p
, end
, OPTION_LEASE_TIME
, 4, renewal_time
);
371 if (renewal_time
!= 0xffffffff)
373 unsigned short fuzz
= rand16();
374 while (fuzz
> (renewal_time
/16))
376 p
= option_put(p
, end
, OPTION_T1
, 4, (renewal_time
/2) - fuzz
);
377 p
= option_put(p
, end
, OPTION_T2
, 4, ((renewal_time
* 7)/8) - fuzz
);
379 p
= do_req_options(context
, p
, end
, req_options
, dhcp_opts
, domain_suffix
,
380 hostname
, router
, iface_addr
, iface_mtu
, netid
);
381 p
= option_put(p
, end
, OPTION_END
, 0, 0);
382 return p
- (unsigned char *)mess
;
385 if (have_config(config
, CONFIG_DISABLE
))
387 log_packet("INFORM", &mess
->ciaddr
, mess
->chaddr
, iface_name
, "ignored");
391 log_packet("INFORM", &mess
->ciaddr
, mess
->chaddr
, iface_name
, NULL
);
393 p
= option_put(p
, end
, OPTION_MESSAGE_TYPE
, 1, DHCPACK
);
394 p
= option_put(p
, end
, OPTION_SERVER_IDENTIFIER
, INADDRSZ
, ntohl(iface_addr
.s_addr
));
395 p
= do_req_options(context
, p
, end
, req_options
, dhcp_opts
, domain_suffix
,
396 hostname
, router
, iface_addr
, iface_mtu
, netid
);
397 p
= option_put(p
, end
, OPTION_END
, 0, 0);
399 log_packet("ACK", &mess
->ciaddr
, mess
->chaddr
, iface_name
, hostname
);
400 return p
- (unsigned char *)mess
;
406 static void log_packet(char *type
, struct in_addr
*addr
, unsigned char *hwaddr
, char *interface
, char *string
)
408 syslog(LOG_INFO
, "DHCP%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s",
412 addr
? inet_ntoa(*addr
) : "",
413 hwaddr
[0], hwaddr
[1], hwaddr
[2], hwaddr
[3], hwaddr
[4], hwaddr
[5],
415 string
? string
: "");
418 static int option_len(unsigned char *opt
)
423 static void *option_ptr(unsigned char *opt
)
428 static struct in_addr
option_addr(unsigned char *opt
)
430 /* this worries about unaligned data in the option. */
431 /* struct in_addr is network byte order */
434 memcpy(&ret
, option_ptr(opt
), INADDRSZ
);
439 static unsigned int option_uint(unsigned char *opt
, int size
)
441 /* this worries about unaligned data and byte order */
442 unsigned int ret
= 0;
444 unsigned char *p
= option_ptr(opt
);
446 for (i
= 0; i
< size
; i
++)
447 ret
= (ret
<< 8) | *p
++;
452 static void bootp_option_put(struct dhcp_packet
*mess
, char *filename
, char *sname
)
454 memset(mess
->sname
, 0, sizeof(mess
->sname
));
455 memset(mess
->file
, 0, sizeof(mess
->file
));
457 strncpy(mess
->sname
, sname
, sizeof(mess
->sname
)-1);
459 strncpy(mess
->file
, filename
, sizeof(mess
->file
)-1);
462 static unsigned char *option_put(unsigned char *p
, unsigned char *end
, int opt
, int len
, unsigned int val
)
466 /* always keep one octet space for the END option. */
467 if ((opt
== OPTION_END
) || (p
+ len
+ 3 < end
))
470 if (opt
!= OPTION_END
)
474 for (i
= 0; i
< len
; i
++)
475 *(p
++) = val
>> (8 * (len
- (i
+ 1)));
481 static unsigned char *option_put_string(unsigned char *p
, unsigned char *end
, int opt
, char *string
)
483 if (p
+ strlen(string
) + 3 < end
)
486 *(p
++) = strlen(string
);
487 memcpy(p
, string
, strlen(string
));
493 static unsigned char *option_find1(unsigned char *p
, unsigned char *end
, int opt
, int *overload
)
498 while (*p
!= OPTION_END
)
500 if (end
&& (p
>= end
))
501 return 0; /* malformed packet */
502 else if (*p
== OPTION_PAD
)
504 else if (*p
== OPTION_OVERLOAD
)
506 if (end
&& (p
>= end
- 3))
507 return 0; /* malformed packet */
515 if (end
&& (p
>= end
- 2))
516 return 0; /* malformed packet */
517 opt_len
= option_len(p
);
518 if (end
&& (p
>= end
- (2 + opt_len
)))
519 return 0; /* malformed packet */
529 static unsigned char *option_find(struct dhcp_packet
*mess
, int size
, int opt_type
)
534 ret
= option_find1(&mess
->options
[0], ((unsigned char *)mess
) + size
, opt_type
, &overload
);
536 if (!ret
&& (overload
& 1))
537 ret
= option_find1(&mess
->file
[0], &mess
->file
[128], opt_type
, &overload
);
539 if (!ret
&& (overload
& 2))
540 ret
= option_find1(&mess
->sname
[0], &mess
->file
[64], opt_type
, &overload
);
545 static int in_list(unsigned char *list
, int opt
)
549 for (i
= 0; list
[i
] != OPTION_END
; i
++)
556 static struct dhcp_opt
*option_find2(char *netid
, struct dhcp_opt
*opts
, int opt
)
558 for (; opts
; opts
= opts
->next
)
559 if (opts
->opt
== opt
&&
560 (!opts
->netid
|| (netid
&& strcmp(opts
->netid
, netid
) == 0)))
565 static unsigned char *do_req_options(struct dhcp_context
*context
,
566 unsigned char *p
, unsigned char *end
,
567 unsigned char *req_options
,
568 struct dhcp_opt
*config_opts
,
569 char *domainname
, char *hostname
,
570 struct in_addr router
,
571 struct in_addr iface_addr
,
572 int iface_mtu
, char *netid
)
579 if (in_list(req_options
, OPTION_MAXMESSAGE
))
580 p
= option_put(p
, end
, OPTION_MAXMESSAGE
, 2,
581 DNSMASQ_PACKETSZ
> iface_mtu
?
582 iface_mtu
: DNSMASQ_PACKETSZ
);
584 if (in_list(req_options
, OPTION_NETMASK
) &&
585 !option_find2(netid
, config_opts
, OPTION_NETMASK
))
586 p
= option_put(p
, end
, OPTION_NETMASK
, INADDRSZ
, ntohl(context
->netmask
.s_addr
));
588 if (in_list(req_options
, OPTION_BROADCAST
) &&
589 !option_find2(netid
, config_opts
, OPTION_BROADCAST
))
590 p
= option_put(p
, end
, OPTION_BROADCAST
, INADDRSZ
, ntohl(context
->broadcast
.s_addr
));
592 if (in_list(req_options
, OPTION_ROUTER
) &&
593 !option_find2(netid
, config_opts
, OPTION_ROUTER
))
594 p
= option_put(p
, end
, OPTION_ROUTER
, INADDRSZ
,
595 ntohl(router
.s_addr
));
597 if (in_list(req_options
, OPTION_DNSSERVER
) &&
598 !option_find2(netid
, config_opts
, OPTION_DNSSERVER
))
599 p
= option_put(p
, end
, OPTION_DNSSERVER
, INADDRSZ
, ntohl(iface_addr
.s_addr
));
601 if (domainname
&& in_list(req_options
, OPTION_DOMAINNAME
) &&
602 !option_find2(netid
, config_opts
, OPTION_DOMAINNAME
))
603 p
= option_put_string(p
, end
, OPTION_DOMAINNAME
, domainname
);
605 /* Note that we ignore attempts to set the hostname using
606 --dhcp-option=12,<name> */
607 if (hostname
&& in_list(req_options
, OPTION_HOSTNAME
))
608 p
= option_put_string(p
, end
, OPTION_HOSTNAME
, hostname
);
610 for (i
= 0; req_options
[i
] != OPTION_END
; i
++)
612 struct dhcp_opt
*opt
;
614 if (req_options
[i
] == OPTION_HOSTNAME
||
615 req_options
[i
] == OPTION_MAXMESSAGE
||
616 !(opt
= option_find2(netid
, config_opts
, req_options
[i
])) ||
617 (p
+ opt
->len
+ 3 >= end
))
620 /* For the options we have default values on
621 dhc-option=<optionno> means "don't include this option"
622 not "include a zero-length option" */
624 (opt
->opt
== OPTION_NETMASK
||
625 opt
->opt
== OPTION_BROADCAST
||
626 opt
->opt
== OPTION_ROUTER
||
627 opt
->opt
== OPTION_DNSSERVER
))
638 struct in_addr
*a
= (struct in_addr
*)opt
->val
;
639 for (j
= 0; j
< opt
->len
; j
+=INADDRSZ
, a
++)
641 /* zero means "self" */
643 memcpy(p
, &iface_addr
, INADDRSZ
);
645 memcpy(p
, a
, INADDRSZ
);
651 memcpy(p
, opt
->val
, opt
->len
);