Add DHPCv6 FQDN support (RFC4704)

Improve hostname support in general, so we are aware of hostname changes.
When setting the hostname, prefer the FQDN name or append a domain.
This commit is contained in:
Roy Marples 2013-06-05 16:23:24 +00:00
parent be2a8dac4e
commit cc3c3560f6
12 changed files with 157 additions and 68 deletions

View File

@ -60,6 +60,7 @@
# define _PATH_DEVNULL "/dev/null" # define _PATH_DEVNULL "/dev/null"
#endif #endif
static char hostname_buffer[HOSTNAME_MAX_LEN];
int clock_monotonic; int clock_monotonic;
static char *lbuf; static char *lbuf;
static size_t lbuf_len; static size_t lbuf_len;
@ -134,6 +135,19 @@ set_nonblock(int fd)
return 0; return 0;
} }
const char *
get_hostname(void)
{
gethostname(hostname_buffer, sizeof(hostname_buffer));
if (strcmp(hostname_buffer, "(none)") == 0 ||
strcmp(hostname_buffer, "localhost") == 0 ||
strncmp(hostname_buffer, "localhost.", strlen("localhost.")) == 0 ||
hostname_buffer[0] == '.')
return NULL;
return hostname_buffer;
}
/* Handy function to get the time. /* Handy function to get the time.
* We only care about time advancements, not the actual time itself * We only care about time advancements, not the actual time itself
* Which is why we use CLOCK_MONOTONIC, but it is not available on all * Which is why we use CLOCK_MONOTONIC, but it is not available on all

View File

@ -34,6 +34,10 @@
#include "config.h" #include "config.h"
#include "defs.h" #include "defs.h"
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#endif
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) #define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6) #define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
@ -96,6 +100,7 @@
int set_cloexec(int); int set_cloexec(int);
int set_nonblock(int); int set_nonblock(int);
char *get_line(FILE * __restrict); char *get_line(FILE * __restrict);
const char *get_hostname(void);
extern int clock_monotonic; extern int clock_monotonic;
int get_monotonic(struct timeval *); int get_monotonic(struct timeval *);
ssize_t setvar(char ***, const char *, const char *, const char *); ssize_t setvar(char ***, const char *, const char *, const char *);

View File

@ -89,6 +89,50 @@ int make_option_mask(const struct dhcp_opt *dopts,
return 0; return 0;
} }
size_t
encode_rfc1035(const char *src, uint8_t *dst)
{
uint8_t *p;
uint8_t *lp;
size_t len;
uint8_t has_dot;
if (src == NULL || *src == '\0')
return 0;
if (dst) {
p = dst;
lp = p++;
}
len = 1;
has_dot = 0;
for (; *src; src++) {
if (*src == '\0')
break;
if (*src == '.') {
/* Skip the trailing . */
if (src[1] == '\0')
break;
has_dot = 1;
if (dst) {
*lp = p - lp - 1;
if (*lp == '\0')
return len;
lp = p++;
}
} else if (dst)
*p++ = (uint8_t)*src;
len++;
}
if (dst) {
*lp = p - lp - 1;
if (has_dot)
*p++ = '\0';
}
if (has_dot)
len++;
return len;
}
/* Decode an RFC3397 DNS search order option into a space /* Decode an RFC3397 DNS search order option into a space
* separated string. Returns length of string (including * separated string. Returns length of string (including
* terminating zero) or zero on error. out may be NULL * terminating zero) or zero on error. out may be NULL

View File

@ -67,6 +67,7 @@ struct dhcp_opt {
#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7))) #define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7))) #define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int); int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
size_t encode_rfc1035(const char *src, uint8_t *dst);
ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *); ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
ssize_t print_string(char *, ssize_t, int, const uint8_t *); ssize_t print_string(char *, ssize_t, int, const uint8_t *);
ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *); ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);

55
dhcp.c
View File

@ -801,33 +801,6 @@ get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp)
return routes; return routes;
} }
static size_t
encode_rfc1035(const char *src, uint8_t *dst)
{
uint8_t *p = dst;
uint8_t *lp = p++;
if (*src == '\0')
return 0;
for (; *src; src++) {
if (*src == '\0')
break;
if (*src == '.') {
/* Skip the trailing . */
if (src[1] == '\0')
break;
*lp = p - lp - 1;
if (*lp == '\0')
return p - dst;
lp = p++;
} else
*p++ = (uint8_t)*src;
}
*lp = p - lp - 1;
*p++ = '\0';
return p - dst;
}
#define PUTADDR(_type, _val) \ #define PUTADDR(_type, _val) \
{ \ { \
*p++ = _type; \ *p++ = _type; \
@ -877,6 +850,7 @@ make_message(struct dhcp_message **message,
const struct dhcp_state *state = D_CSTATE(iface); const struct dhcp_state *state = D_CSTATE(iface);
const struct dhcp_lease *lease = &state->lease; const struct dhcp_lease *lease = &state->lease;
time_t up = uptime() - state->start_uptime; time_t up = uptime() - state->start_uptime;
const char *hostname;
dhcp = calloc(1, sizeof (*dhcp)); dhcp = calloc(1, sizeof (*dhcp));
if (dhcp == NULL) if (dhcp == NULL)
@ -1006,18 +980,22 @@ make_message(struct dhcp_message **message,
* upto the first dot (the short hostname) as otherwise * upto the first dot (the short hostname) as otherwise
* confuses some DHCP servers when updating DNS. * confuses some DHCP servers when updating DNS.
* The FQDN option should be used if a FQDN is required. */ * The FQDN option should be used if a FQDN is required. */
if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) { if (ifo->hostname[0] == '\0')
*p++ = DHO_HOSTNAME; hostname = get_hostname();
hp = strchr(ifo->hostname, '.');
if (hp)
len = hp - ifo->hostname;
else else
len = strlen(ifo->hostname); hostname = ifo->hostname;
if (ifo->options & DHCPCD_HOSTNAME && hostname) {
*p++ = DHO_HOSTNAME;
hp = strchr(hostname, '.');
if (hp)
len = hp - hostname;
else
len = strlen(hostname);
*p++ = len; *p++ = len;
memcpy(p, ifo->hostname, len); memcpy(p, hostname, len);
p += len; p += len;
} }
if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) { if (ifo->fqdn != FQDN_DISABLE) {
/* IETF DHC-FQDN option (81), RFC4702 */ /* IETF DHC-FQDN option (81), RFC4702 */
*p++ = DHO_FQDN; *p++ = DHO_FQDN;
lp = p; lp = p;
@ -1032,13 +1010,18 @@ make_message(struct dhcp_message **message,
* N: 1 => Client requests Server to not * N: 1 => Client requests Server to not
* update DNS * update DNS
*/ */
if (hostname)
*p++ = (ifo->fqdn & 0x09) | 0x04; *p++ = (ifo->fqdn & 0x09) | 0x04;
else
*p++ = (FQDN_NONE & 0x09) | 0x04;
*p++ = 0; /* from server for PTR RR */ *p++ = 0; /* from server for PTR RR */
*p++ = 0; /* from server for A RR if S=1 */ *p++ = 0; /* from server for A RR if S=1 */
ul = encode_rfc1035(ifo->hostname, p); if (hostname) {
ul = encode_rfc1035(hostname, p);
*lp += ul; *lp += ul;
p += ul; p += ul;
} }
}
/* vendor is already encoded correctly, so just add it */ /* vendor is already encoded correctly, so just add it */
if (ifo->vendor[0]) { if (ifo->vendor[0]) {

37
dhcp6.c
View File

@ -126,6 +126,7 @@ const struct dhcp_opt dhcp6_opts[] = {
{ D6_OPTION_BCMS_SERVER_A, IPV6A, "bcms_server_a" }, { D6_OPTION_BCMS_SERVER_A, IPV6A, "bcms_server_a" },
{ D6_OPTION_POSIX_TIMEZONE, STRING, "posix_timezone" }, { D6_OPTION_POSIX_TIMEZONE, STRING, "posix_timezone" },
{ D6_OPTION_TZDB_TIMEZONE, STRING, "tzdb_timezone" }, { D6_OPTION_TZDB_TIMEZONE, STRING, "tzdb_timezone" },
{ D6_OPTION_FQDN, RFC3397, "fqdn" },
{ 0, 0, NULL } { 0, 0, NULL }
}; };
@ -361,6 +362,7 @@ dhcp6_makemessage(struct interface *ifp)
uint8_t IA, *p; uint8_t IA, *p;
uint32_t u32; uint32_t u32;
const struct ipv6_addr *ap; const struct ipv6_addr *ap;
const char *hostname;
state = D6_STATE(ifp); state = D6_STATE(ifp);
if (state->send) { if (state->send) {
@ -379,8 +381,16 @@ dhcp6_makemessage(struct interface *ifp)
len += sizeof(*u16); len += sizeof(*u16);
} }
if (len == 0) if (len == 0)
len = sizeof(*u16) * 3; len = sizeof(*u16) * 4;
len += sizeof(*o); len += sizeof(*o);
if (ifo->fqdn != FQDN_DISABLE) {
if (ifo->hostname[0] == '\0')
hostname = get_hostname();
else
hostname = ifo->hostname;
len += sizeof(*o) + 1 + encode_rfc1035(hostname, NULL);
}
} }
len += sizeof(*state->send); len += sizeof(*state->send);
@ -568,6 +578,26 @@ dhcp6_makemessage(struct interface *ifp)
} }
if (state->send->type != DHCP6_RELEASE) { if (state->send->type != DHCP6_RELEASE) {
if (ifo->fqdn != FQDN_DISABLE) {
o = D6_NEXT_OPTION(o);
o->code = htons(D6_OPTION_FQDN);
p = D6_OPTION_DATA(o);
switch (ifo->fqdn) {
case FQDN_BOTH:
*p = 0x01;
break;
case FQDN_PTR:
*p = 0x00;
break;
default:
*p = 0x04;
break;
}
o->len = encode_rfc1035(hostname, p + 1);
if (o->len == 0)
*p = 0x04;
o->len = htons(++o->len);
}
o = D6_NEXT_OPTION(o); o = D6_NEXT_OPTION(o);
o->code = htons(D6_OPTION_ORO); o->code = htons(D6_OPTION_ORO);
o->len = 0; o->len = 0;
@ -581,10 +611,11 @@ dhcp6_makemessage(struct interface *ifp)
} }
} }
if (o->len == 0) { if (o->len == 0) {
*u16++ = htons(D6_OPTION_UNICAST);
*u16++ = htons(D6_OPTION_DNS_SERVERS); *u16++ = htons(D6_OPTION_DNS_SERVERS);
*u16++ = htons(D6_OPTION_DOMAIN_LIST); *u16++ = htons(D6_OPTION_DOMAIN_LIST);
*u16++ = htons(D6_OPTION_UNICAST); *u16++ = htons(D6_OPTION_FQDN);
o->len = sizeof(*u16) * 3; o->len = sizeof(*u16) * 4;
} }
o->len = htons(o->len); o->len = htons(o->len);
} }

View File

@ -79,6 +79,7 @@
#define D6_OPTION_INFO_REFRESH_TIME 32 #define D6_OPTION_INFO_REFRESH_TIME 32
#define D6_OPTION_BCMS_SERVER_D 33 #define D6_OPTION_BCMS_SERVER_D 33
#define D6_OPTION_BCMS_SERVER_A 34 #define D6_OPTION_BCMS_SERVER_A 34
#define D6_OPTION_FQDN 39
#define D6_OPTION_POSIX_TIMEZONE 41 #define D6_OPTION_POSIX_TIMEZONE 41
#define D6_OPTION_TZDB_TIMEZONE 42 #define D6_OPTION_TZDB_TIMEZONE 42

View File

@ -65,15 +65,27 @@ try_hostname()
set_hostname() set_hostname()
{ {
if need_hostname; then
if [ -n "$new_host_name" ]; then need_hostname || return
try_hostname "$new_host_name"
elif [ -n "$new_fqdn_name" ]; then if [ -n "$new_fqdn_name" ]; then
try_hostname "$new_fqdn_name" try_hostname "$new_fqdn_name"
elif [ -n "$new_host_name" ]; then
if [ -n "$new_domain_name" ]; then
try_hostname "$new_host_name.$new_domain_name"
else
try_hostname "$new_host_name"
fi fi
fi fi
} }
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
new_fqdn_name="$new_dhcp6_fqdn"
;;
esac
if $if_up; then if $if_up; then
set_hostname set_hostname
fi fi

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd May 3, 2013 .Dd June 5, 2013
.Dt DHCPCD 8 .Dt DHCPCD 8
.Os .Os
.Sh NAME .Sh NAME
@ -605,7 +605,7 @@ running on the
.Sh STANDARDS .Sh STANDARDS
RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361, RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361,
RFC 3633, RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 3633, RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702,
RFC 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106. RFC 4704, RFC 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106.
.Sh AUTHORS .Sh AUTHORS
.An Roy Marples Aq roy@marples.name .An Roy Marples Aq roy@marples.name
.Sh BUGS .Sh BUGS

View File

@ -180,9 +180,11 @@ IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
Only configure IPv4. Only configure IPv4.
.It Ic ipv6only .It Ic ipv6only
Only confgiure IPv6. Only confgiure IPv6.
.It Ic fqdn Op none | ptr | both .It Ic fqdn Op disable | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record. record of the host in DNS whereas both also updates the A record.
disable will disable the FQDN option.
The default is both.
.Nm dhcpcd .Nm dhcpcd
itself never does any DNS updates. itself never does any DNS updates.
.Nm dhcpcd .Nm dhcpcd

View File

@ -459,20 +459,20 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
add_environ(ifo, arg, 1); add_environ(ifo, arg, 1);
break; break;
case 'h': case 'h':
if (arg) { if (!arg) {
s = parse_string(ifo->hostname, ifo->options |= DHCPCD_HOSTNAME;
HOSTNAME_MAX_LEN, arg); break;
}
s = parse_string(ifo->hostname, HOSTNAME_MAX_LEN, arg);
if (s == -1) { if (s == -1) {
syslog(LOG_ERR, "hostname: %m"); syslog(LOG_ERR, "hostname: %m");
return -1; return -1;
} }
if (s != 0 && ifo->hostname[0] == '.') { if (s != 0 && ifo->hostname[0] == '.') {
syslog(LOG_ERR, syslog(LOG_ERR, "hostname cannot begin with .");
"hostname cannot begin with .");
return -1; return -1;
} }
ifo->hostname[s] = '\0'; ifo->hostname[s] = '\0';
}
if (ifo->hostname[0] == '\0') if (ifo->hostname[0] == '\0')
ifo->options &= ~DHCPCD_HOSTNAME; ifo->options &= ~DHCPCD_HOSTNAME;
else else
@ -1131,12 +1131,6 @@ read_config(const char *file,
ifo->reboot = DEFAULT_REBOOT; ifo->reboot = DEFAULT_REBOOT;
ifo->metric = -1; ifo->metric = -1;
strlcpy(ifo->script, SCRIPT, sizeof(ifo->script)); strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
gethostname(ifo->hostname, HOSTNAME_MAX_LEN);
/* Ensure that the hostname is NULL terminated */
ifo->hostname[HOSTNAME_MAX_LEN] = '\0';
if (strcmp(ifo->hostname, "(none)") == 0 ||
strcmp(ifo->hostname, "localhost") == 0)
ifo->hostname[0] = '\0';
ifo->vendorclassid[0] = strlen(vendor); ifo->vendorclassid[0] = strlen(vendor);
memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]); memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]);

View File

@ -45,7 +45,9 @@
#define DEFAULT_TIMEOUT 30 #define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 5 #define DEFAULT_REBOOT 5
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */ #define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#endif
#define VENDORCLASSID_MAX_LEN 255 #define VENDORCLASSID_MAX_LEN 255
#define CLIENTID_MAX_LEN 48 #define CLIENTID_MAX_LEN 48
#define USERCLASS_MAX_LEN 255 #define USERCLASS_MAX_LEN 255