2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-31 06:15:55 +00:00

-n [master]

Add code to support the standards version of DDNS
This commit is contained in:
Shawn Routhier
2013-10-14 15:53:24 -07:00
parent 1d851cff2c
commit d7d9c0c7c3
38 changed files with 2630 additions and 2114 deletions

View File

@@ -62,6 +62,8 @@ struct sockaddr_in sockaddr_broadcast;
struct in_addr giaddr;
struct data_string default_duid;
int duid_type = 0;
int duid_v4 = 0;
int std_dhcid = 0;
/* ASSERT_STATE() does nothing now; it used to be
assert (state_is == state_shouldbe). */
@@ -287,12 +289,9 @@ main(int argc, char **argv) {
wanted_ia_na = 0;
}
wanted_ia_pd++;
#endif /* DHCPv6 */
} else if (!strcmp(argv[i], "-D")) {
if (local_family_set && (local_family == AF_INET)) {
usage();
}
local_family_set = 1;
local_family = AF_INET6;
duid_v4 = 1;
if (++i == argc)
usage();
if (!strcasecmp(argv[i], "LL")) {
@@ -302,7 +301,12 @@ main(int argc, char **argv) {
} else {
usage();
}
#endif /* DHCPv6 */
} else if (!strcmp(argv[i], "-i")) {
/* enable DUID support for DHCPv4 clients */
duid_v4 = 1;
} else if (!strcmp(argv[i], "-I")) {
/* enable standard DHCID support for DDNS updates */
std_dhcid = 1;
} else if (!strcmp(argv[i], "-v")) {
quiet = 0;
} else if (!strcmp(argv[i], "--version")) {
@@ -564,12 +568,13 @@ main(int argc, char **argv) {
}
srandom(seed + cur_time + (unsigned)getpid());
/* Start a configuration state machine for each interface. */
#ifdef DHCPv6
if (local_family == AF_INET6) {
/* Establish a default DUID. This may be moved to the
* DHCPv4 area later.
*/
/*
* Establish a default DUID. We always do so for v6 and
* do so if desired for v4 via the -D or -i options
*/
if ((local_family == AF_INET6) ||
((local_family == AF_INET) && (duid_v4 == 1))) {
if (default_duid.len == 0) {
if (default_duid.buffer != NULL)
data_string_forget(&default_duid, MDL);
@@ -577,7 +582,11 @@ main(int argc, char **argv) {
form_duid(&default_duid, MDL);
write_duid(&default_duid);
}
}
/* Start a configuration state machine for each interface. */
#ifdef DHCPv6
if (local_family == AF_INET6) {
for (ip = interfaces ; ip != NULL ; ip = ip->next) {
for (client = ip->client ; client != NULL ;
client = client->next) {
@@ -709,9 +718,9 @@ static void usage()
log_fatal("Usage: dhclient "
#ifdef DHCPv6
"[-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]\n"
"[-4|-6] [-SNTPI1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n"
#else /* DHCPv6 */
"[-1dvrx] [-nw] [-p <port>]\n"
"[-I1dvrxi] [-nw] [-p <port>] [-D LL|LLT] \n"
#endif /* DHCPv6 */
" [-s server-addr] [-cf config-file] "
"[-lf lease-file]\n"
@@ -2296,24 +2305,24 @@ make_client_options(struct client_state *client, struct client_lease *lease,
unsigned i;
struct option_cache *oc;
struct option *option = NULL;
struct buffer *bp = (struct buffer *)0;
struct buffer *bp = NULL;
/* If there are any leftover options, get rid of them. */
if (*op)
option_state_dereference (op, MDL);
option_state_dereference(op, MDL);
/* Allocate space for options. */
option_state_allocate (op, MDL);
option_state_allocate(op, MDL);
/* Send the server identifier if provided. */
if (sid)
save_option (&dhcp_universe, *op, sid);
save_option(&dhcp_universe, *op, sid);
oc = (struct option_cache *)0;
oc = NULL;
/* Send the requested address if provided. */
if (rip) {
client -> requested_address = *rip;
client->requested_address = *rip;
i = DHO_DHCP_REQUESTED_ADDRESS;
if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
&i, 0, MDL) &&
@@ -2321,22 +2330,22 @@ make_client_options(struct client_state *client, struct client_lease *lease,
option, MDL)))
log_error ("can't make requested address cache.");
else {
save_option (&dhcp_universe, *op, oc);
option_cache_dereference (&oc, MDL);
save_option(&dhcp_universe, *op, oc);
option_cache_dereference(&oc, MDL);
}
option_dereference(&option, MDL);
} else {
client -> requested_address.len = 0;
client->requested_address.len = 0;
}
i = DHO_DHCP_MESSAGE_TYPE;
if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0,
MDL) &&
make_const_option_cache(&oc, NULL, type, 1, option, MDL)))
log_error ("can't make message type.");
log_error("can't make message type.");
else {
save_option (&dhcp_universe, *op, oc);
option_cache_dereference (&oc, MDL);
save_option(&dhcp_universe, *op, oc);
option_cache_dereference(&oc, MDL);
}
option_dereference(&option, MDL);
@@ -2349,8 +2358,8 @@ make_client_options(struct client_state *client, struct client_lease *lease,
if (prl[i]->universe == &dhcp_universe)
len++;
if (!buffer_allocate (&bp, len, MDL))
log_error ("can't make parameter list buffer.");
if (!buffer_allocate(&bp, len, MDL))
log_error("can't make parameter list buffer.");
else {
unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST;
@@ -2364,15 +2373,69 @@ make_client_options(struct client_state *client, struct client_lease *lease,
&code, 0, MDL) &&
make_const_option_cache(&oc, &bp, NULL, len,
option, MDL)))
log_error ("can't make option cache");
log_error("can't make option cache");
else {
save_option (&dhcp_universe, *op, oc);
option_cache_dereference (&oc, MDL);
save_option(&dhcp_universe, *op, oc);
option_cache_dereference(&oc, MDL);
}
option_dereference(&option, MDL);
}
}
/*
* If requested (duid_v4 == 1) add an RFC4361 compliant client-identifier
* This can be overridden by including a client id in the configuration
* file.
*/
if (duid_v4 == 1) {
struct data_string client_identifier;
int hw_idx, hw_len;
memset(&client_identifier, 0, sizeof(client_identifier));
client_identifier.len = 1 + 4 + default_duid.len;
if (!buffer_allocate(&client_identifier.buffer,
client_identifier.len, MDL))
log_fatal("no memory for default DUID!");
client_identifier.data = client_identifier.buffer->data;
i = DHO_DHCP_CLIENT_IDENTIFIER;
/* Client-identifier type : 1 byte */
*client_identifier.buffer->data = 255;
/* IAID : 4 bytes
* we use the low 4 bytes from the interface address
*/
if (client->interface->hw_address.hlen > 4) {
hw_idx = client->interface->hw_address.hlen - 4;
hw_len = 4;
} else {
hw_idx = 0;
hw_len = client->interface->hw_address.hlen;
}
memcpy(&client_identifier.buffer->data + 5 - hw_len,
client->interface->hw_address.hbuf + hw_idx,
hw_len);
/* Add the default duid */
memcpy(&client_identifier.buffer->data+(1+4),
default_duid.data, default_duid.len);
/* And save the option */
if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
&i, 0, MDL) &&
make_const_option_cache(&oc, NULL,
(u_int8_t *)client_identifier.data,
client_identifier.len,
option, MDL)))
log_error ("can't make requested client id cache..");
else {
save_option (&dhcp_universe, *op, oc);
option_cache_dereference (&oc, MDL);
}
option_dereference(&option, MDL);
}
/* Run statements that need to be run on transmission. */
if (client->config->on_transmission)
execute_statements_in_scope(NULL, NULL, NULL, client,
@@ -2778,6 +2841,69 @@ write_options(struct client_state *client, struct option_state *options,
}
}
/*
* The "best" default DUID, since we cannot predict any information
* about the system (such as whether or not the hardware addresses are
* integrated into the motherboard or similar), is the "LLT", link local
* plus time, DUID. For real stateless "LL" is better.
*
* Once generated, this duid is stored into the state database, and
* retained across restarts.
*
* For the time being, there is probably a different state database for
* every daemon, so this winds up being a per-interface identifier...which
* is not how it is intended. Upcoming rearchitecting the client should
* address this "one daemon model."
*/
void
form_duid(struct data_string *duid, const char *file, int line)
{
struct interface_info *ip;
int len;
/* For now, just use the first interface on the list. */
ip = interfaces;
if (ip == NULL)
log_fatal("Impossible condition at %s:%d.", MDL);
if ((ip->hw_address.hlen == 0) ||
(ip->hw_address.hlen > sizeof(ip->hw_address.hbuf)))
log_fatal("Impossible hardware address length at %s:%d.", MDL);
if (duid_type == 0)
duid_type = stateless ? DUID_LL : DUID_LLT;
/*
* 2 bytes for the 'duid type' field.
* 2 bytes for the 'htype' field.
* (DUID_LLT) 4 bytes for the 'current time'.
* enough bytes for the hardware address (note that hw_address has
* the 'htype' on byte zero).
*/
len = 4 + (ip->hw_address.hlen - 1);
if (duid_type == DUID_LLT)
len += 4;
if (!buffer_allocate(&duid->buffer, len, MDL))
log_fatal("no memory for default DUID!");
duid->data = duid->buffer->data;
duid->len = len;
/* Basic Link Local Address type of DUID. */
if (duid_type == DUID_LLT) {
putUShort(duid->buffer->data, DUID_LLT);
putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH);
memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
ip->hw_address.hlen - 1);
} else {
putUShort(duid->buffer->data, DUID_LL);
putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1,
ip->hw_address.hlen - 1);
}
}
/* Write the default DUID to the lease store. */
static isc_result_t
write_duid(struct data_string *duid)
@@ -3991,6 +4117,7 @@ client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
struct option_cache *oc;
int ignorep;
int result;
int ddns_v4_type;
isc_result_t rcode;
/* If we didn't send an FQDN option, we certainly aren't going to
@@ -4033,48 +4160,82 @@ client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
&global_scope, oc, MDL))
return ISC_R_SUCCESS;
/* If this is a DHCPv6 client update, make a dhcid string out of
* the DUID. If this is a DHCPv4 client update, choose either
* the client identifier, if there is one, or the interface's
* MAC address.
/*
* Construct the DHCID value for use in the DDNS update process
* We have the newer standard version and the older interim version
* chosen by the '-I' option. The interim version is left as is
* for backwards compatibility. The standard version is based on
* RFC 4701 section 3.3
*/
result = 0;
POST(result);
memset(&client_identifier, 0, sizeof(client_identifier));
if (std_dhcid == 1) {
/* standard style */
ddns_cb->dhcid_class = dns_rdatatype_dhcid;
ddns_v4_type = 1;
} else {
/* interim style */
ddns_cb->dhcid_class = dns_rdatatype_txt;
/* for backwards compatibility */
ddns_v4_type = DHO_DHCP_CLIENT_IDENTIFIER;
}
if (client->active_lease != NULL) {
if (((oc =
lookup_option(&dhcpv6_universe, client->sent_options,
D6O_CLIENTID)) != NULL) &&
evaluate_option_cache(&client_identifier, NULL, NULL,
client, client->sent_options, NULL,
/* V6 request, get the client identifier, then
* construct the dhcid for either standard
* or interim */
if (((oc = lookup_option(&dhcpv6_universe,
client->sent_options,
D6O_CLIENTID)) != NULL) &&
evaluate_option_cache(&client_identifier, NULL,
NULL, client,
client->sent_options, NULL,
&global_scope, oc, MDL)) {
/* RFC4701 defines type '2' as being for the DUID
* field. We aren't using RFC4701 DHCID RR's yet,
* but this is as good a value as any.
*/
result = get_dhcid(&ddns_cb->dhcid, 2,
result = get_dhcid(ddns_cb, 2,
client_identifier.data,
client_identifier.len);
data_string_forget(&client_identifier, MDL);
} else
log_fatal("Impossible condition at %s:%d.", MDL);
} else {
if (((oc =
lookup_option(&dhcp_universe, client->sent_options,
DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
evaluate_option_cache(&client_identifier, NULL, NULL,
client, client->sent_options, NULL,
/*
* V4 request, use the client id if there is one or the
* mac address if there isn't. If we have a client id
* we check to see if it is an embedded DUID.
*/
if (((oc = lookup_option(&dhcp_universe,
client->sent_options,
DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
evaluate_option_cache(&client_identifier, NULL,
NULL, client,
client->sent_options, NULL,
&global_scope, oc, MDL)) {
result = get_dhcid(&ddns_cb->dhcid,
DHO_DHCP_CLIENT_IDENTIFIER,
client_identifier.data,
client_identifier.len);
if ((std_dhcid == 1) && (duid_v4 == 1) &&
(client_identifier.data[0] == 255)) {
/*
* This appears to be an embedded DUID,
* extract it and treat it as such
*/
if (client_identifier.len <= 5)
log_fatal("Impossible condition at %s:%d.",
MDL);
result = get_dhcid(ddns_cb, 2,
client_identifier.data + 5,
client_identifier.len - 5);
} else {
result = get_dhcid(ddns_cb, ddns_v4_type,
client_identifier.data,
client_identifier.len);
}
data_string_forget(&client_identifier, MDL);
} else
result = get_dhcid(&ddns_cb->dhcid, 0,
result = get_dhcid(ddns_cb, 0,
client->interface->hw_address.hbuf,
client->interface->hw_address.hlen);
}
if (!result) {
return ISC_R_SUCCESS;
}
@@ -4342,3 +4503,4 @@ add_reject(struct packet *packet) {
*/
log_info("Server added to list of rejected servers.");
}