diff --git a/CHANGELOG b/CHANGELOG index b32d95d..955405b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +version 2.80 + Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method + for the initial patch and motivation. + + version 2.79 Fix parsing of CNAME arguments, which are confused by extra spaces. Thanks to Diego Aguirre for spotting the bug. diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example index 574b053..008ddb7 100644 --- a/dnsmasq.conf.example +++ b/dnsmasq.conf.example @@ -547,6 +547,14 @@ # http://www.isc.org/files/auth.html #dhcp-authoritative +# Set the DHCP server to enable DHCPv4 Rapid Commit Option per RFC 4039. +# In this mode it will respond to a DHCPDISCOVER message including a Rapid Commit +# option with a DHCPACK including a Rapid Commit option and fully committed address +# and configuration information. This must only be enabled if either the server is +# the only server for the subnet, or multiple servers are present and they each +# commit a binding for all clients. +#dhcp-rapid-commit + # Run an executable when a DHCP lease is created or destroyed. # The arguments sent to the script are "add" or "del", # then the MAC address, the IP address and finally the hostname diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index bd99b48..e2dc65c 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -1452,6 +1452,13 @@ allows dnsmasq to rebuild its lease database without each client needing to reacquire a lease, if the database is lost. For DHCPv6 it sets the priority in replies to 255 (the maximum) instead of 0 (the minimum). .TP +.B --dhcp-rapid-commit +Enable DHCPv4 Rapid Commit Option specified in RFC 4039. When enabled, dnsmasq +will respond to a DHCPDISCOVER message including a Rapid Commit +option with a DHCPACK including a Rapid Commit option and fully committed +address and configuration information. Should only be enabled if either the +server is the only server for the subnet, or multiple servers are present and they each commit a binding for all clients. +.TP .B --dhcp-alternate-port[=[,]] (IPv4 only) Change the ports used for DHCP from the default. If this option is given alone, without arguments, it changes the ports used for DHCP diff --git a/src/dhcp-common.c b/src/dhcp-common.c index d9719d1..8ff0f0d 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -556,6 +556,7 @@ static const struct opttab_t { { "nntp-server", 71, OT_ADDR_LIST }, { "irc-server", 74, OT_ADDR_LIST }, { "user-class", 77, 0 }, + { "rapid-commit", 80, 0 }, { "FQDN", 81, OT_INTERNAL }, { "agent-id", 82, OT_INTERNAL }, { "client-arch", 93, 2 | OT_DEC }, diff --git a/src/dhcp-protocol.h b/src/dhcp-protocol.h index a4a3535..389c85e 100644 --- a/src/dhcp-protocol.h +++ b/src/dhcp-protocol.h @@ -54,6 +54,7 @@ #define OPTION_SNAME 66 #define OPTION_FILENAME 67 #define OPTION_USER_CLASS 77 +#define OPTION_RAPID_COMMIT 80 #define OPTION_CLIENT_FQDN 81 #define OPTION_AGENT_ID 82 #define OPTION_ARCH 93 diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 6773b69..30c4519 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -250,7 +250,8 @@ struct event_desc { #define OPT_MAC_B64 54 #define OPT_MAC_HEX 55 #define OPT_TFTP_APREF_MAC 56 -#define OPT_LAST 57 +#define OPT_RAPID_COMMIT 57 +#define OPT_LAST 58 /* extra flags for my_syslog, we use a couple of facilities since they are known not to occupy the same bits as priorities, no matter how syslog.h is set up. */ diff --git a/src/option.c b/src/option.c index d358d99..2667f5b 100644 --- a/src/option.c +++ b/src/option.c @@ -160,6 +160,7 @@ struct myoption { #define LOPT_DHCPTTL 348 #define LOPT_TFTP_MTU 349 #define LOPT_REPLY_DELAY 350 +#define LOPT_RAPID_COMMIT 351 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -325,6 +326,7 @@ static const struct myoption opts[] = { "script-arp", 0, 0, LOPT_SCRIPT_ARP }, { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL }, { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY }, + { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT }, { NULL, 0, 0, 0 } }; @@ -497,6 +499,7 @@ static struct { { LOPT_IGNORE_ADDR, ARG_DUP, "", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, { LOPT_DHCPTTL, ARG_ONE, "", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, { LOPT_REPLY_DELAY, ARG_ONE, "", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL }, + { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL }, { 0, 0, NULL, NULL, NULL } }; diff --git a/src/rfc2131.c b/src/rfc2131.c index c08a8ab..5416f3e 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -75,7 +75,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, struct dhcp_vendor *vendor; struct dhcp_mac *mac; struct dhcp_netid_list *id_list; - int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1; + int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1; struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base; unsigned char *end = (unsigned char *)(mess + 1); unsigned char *real_end = (unsigned char *)(mess + 1); @@ -1073,6 +1073,13 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, log_tags(tagif_netid, ntohl(mess->xid)); apply_delay(mess->xid, recvtime, tagif_netid); + + if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0)) + { + rapid_commit = 1; + goto rapid_commit; + } + log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid); time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4)); @@ -1085,7 +1092,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz); return dhcp_packet_size(mess, agent_id, real_end); - + + case DHCPREQUEST: if (ignore || have_config(config, CONFIG_DISABLE)) return 0; @@ -1183,9 +1191,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, fuzz = rand16(); mess->yiaddr = mess->ciaddr; } - + log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid); - + + rapid_commit: if (!message) { struct dhcp_config *addr_config; @@ -1256,7 +1265,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, if (message) { - log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid); + log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid); + + /* rapid commit case: lease allocate failed but don't send DHCPNAK */ + if (rapid_commit) + return 0; mess->yiaddr.s_addr = 0; clear_packet(mess, end); @@ -1414,12 +1427,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, override = lease->override; log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid); - + clear_packet(mess, end); option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK); option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr)); option_put(mess, end, OPTION_LEASE_TIME, 4, time); - do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), + if (rapid_commit) + option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0); + do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz); }