]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add DHPCv6 FQDN support (RFC4704)
authorRoy Marples <roy@marples.name>
Wed, 5 Jun 2013 16:23:24 +0000 (16:23 +0000)
committerRoy Marples <roy@marples.name>
Wed, 5 Jun 2013 16:23:24 +0000 (16:23 +0000)
Improve hostname support in general, so we are aware of hostname changes.
When setting the hostname, prefer the FQDN name or append a domain.

12 files changed:
common.c
common.h
dhcp-common.c
dhcp-common.h
dhcp.c
dhcp6.c
dhcp6.h
dhcpcd-hooks/30-hostname
dhcpcd.8.in
dhcpcd.conf.5.in
if-options.c
if-options.h

index ab3a058094565d294f8498ed115f0278aaeb25a6..4f277958e2cae4b5183e3a2634dab7119271fd79 100644 (file)
--- a/common.c
+++ b/common.c
@@ -60,6 +60,7 @@
 #  define _PATH_DEVNULL "/dev/null"
 #endif
 
+static char hostname_buffer[HOSTNAME_MAX_LEN];
 int clock_monotonic;
 static char *lbuf;
 static size_t lbuf_len;
@@ -134,6 +135,19 @@ set_nonblock(int fd)
        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.
  * 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
index 0e5bd04125e637867b1d835963cb573f1d5e0352..b7851cb456bd242aa2a22a7414e1658b922f1e3b 100644 (file)
--- a/common.h
+++ b/common.h
 #include "config.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 timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
 int set_cloexec(int);
 int set_nonblock(int);
 char *get_line(FILE * __restrict);
+const char *get_hostname(void);
 extern int clock_monotonic;
 int get_monotonic(struct timeval *);
 ssize_t setvar(char ***, const char *, const char *, const char *);
index 6c9ab7809aca8b4b39e8c42777a334556c9d23e6..d8c5ae62d9e0cc531e2122596025b2aa227b32ba 100644 (file)
@@ -89,6 +89,50 @@ int make_option_mask(const struct dhcp_opt *dopts,
        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
  * separated string. Returns length of string (including
  * terminating zero) or zero on error. out may be NULL
index 50e69b96684745f9bcbfab745e8a385a553f92c4..22f98bc13f0eff57d5b507693480f1b711a6f949 100644 (file)
@@ -67,6 +67,7 @@ struct dhcp_opt {
 #define del_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);
+size_t encode_rfc1035(const char *src, uint8_t *dst);
 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_option(char *, ssize_t, int, int, const uint8_t *, const char *);
diff --git a/dhcp.c b/dhcp.c
index 5514f3aadacbe8d0d8faac5be4e58659a8fa4188..8180a40ee02b75f7dd3215ae8c33edd98b0a5d67 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -801,33 +801,6 @@ get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp)
        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)                                                 \
        {                                                                     \
                *p++ = _type;                                                 \
@@ -877,6 +850,7 @@ make_message(struct dhcp_message **message,
        const struct dhcp_state *state = D_CSTATE(iface);
        const struct dhcp_lease *lease = &state->lease;
        time_t up = uptime() - state->start_uptime;
+       const char *hostname;
 
        dhcp = calloc(1, sizeof (*dhcp));
        if (dhcp == NULL)
@@ -1006,18 +980,22 @@ make_message(struct dhcp_message **message,
                 * upto the first dot (the short hostname) as otherwise
                 * confuses some DHCP servers when updating DNS.
                 * The FQDN option should be used if a FQDN is required. */
-               if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
+               if (ifo->hostname[0] == '\0')
+                       hostname = get_hostname();
+               else
+                       hostname = ifo->hostname;
+               if (ifo->options & DHCPCD_HOSTNAME && hostname) {
                        *p++ = DHO_HOSTNAME;
-                       hp = strchr(ifo->hostname, '.');
+                       hp = strchr(hostname, '.');
                        if (hp)
-                               len = hp - ifo->hostname;
+                               len = hp - hostname;
                        else
-                               len = strlen(ifo->hostname);
+                               len = strlen(hostname);
                        *p++ = len;
-                       memcpy(p, ifo->hostname, len);
+                       memcpy(p, hostname, len);
                        p += len;
                }
-               if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
+               if (ifo->fqdn != FQDN_DISABLE) {
                        /* IETF DHC-FQDN option (81), RFC4702 */
                        *p++ = DHO_FQDN;
                        lp = p;
@@ -1032,12 +1010,17 @@ make_message(struct dhcp_message **message,
                         * N: 1 => Client requests Server to not
                         *         update DNS
                         */
-                       *p++ = (ifo->fqdn & 0x09) | 0x04;
+                       if (hostname)
+                               *p++ = (ifo->fqdn & 0x09) | 0x04;
+                       else
+                               *p++ = (FQDN_NONE & 0x09) | 0x04;
                        *p++ = 0; /* from server for PTR RR */
                        *p++ = 0; /* from server for A RR if S=1 */
-                       ul = encode_rfc1035(ifo->hostname, p);
-                       *lp += ul;
-                       p += ul;
+                       if (hostname) {
+                               ul = encode_rfc1035(hostname, p);
+                               *lp += ul;
+                               p += ul;
+                       }
                }
 
                /* vendor is already encoded correctly, so just add it */
diff --git a/dhcp6.c b/dhcp6.c
index 026cc6c384d0a9657fe0f80134e5b51ab955ab0b..b0fe4393bd99dccb245d6b50f7ff92bea570f182 100644 (file)
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -126,6 +126,7 @@ const struct dhcp_opt dhcp6_opts[] = {
        { D6_OPTION_BCMS_SERVER_A,      IPV6A,          "bcms_server_a" },
        { D6_OPTION_POSIX_TIMEZONE,     STRING,         "posix_timezone" },
        { D6_OPTION_TZDB_TIMEZONE,      STRING,         "tzdb_timezone" },
+       { D6_OPTION_FQDN,               RFC3397,        "fqdn" },
        { 0, 0, NULL }
 };
 
@@ -361,6 +362,7 @@ dhcp6_makemessage(struct interface *ifp)
        uint8_t IA, *p;
        uint32_t u32;
        const struct ipv6_addr *ap;
+       const char *hostname;
 
        state = D6_STATE(ifp);
        if (state->send) {
@@ -379,8 +381,16 @@ dhcp6_makemessage(struct interface *ifp)
                                len += sizeof(*u16);
                }
                if (len == 0)
-                       len = sizeof(*u16) * 3;
+                       len = sizeof(*u16) * 4;
                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);
@@ -568,6 +578,26 @@ dhcp6_makemessage(struct interface *ifp)
        }
 
        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->code = htons(D6_OPTION_ORO);
                o->len = 0;
@@ -581,10 +611,11 @@ dhcp6_makemessage(struct interface *ifp)
                        }
                }
                if (o->len == 0) {
+                       *u16++ = htons(D6_OPTION_UNICAST);
                        *u16++ = htons(D6_OPTION_DNS_SERVERS);
                        *u16++ = htons(D6_OPTION_DOMAIN_LIST);
-                       *u16++ = htons(D6_OPTION_UNICAST);
-                       o->len = sizeof(*u16) * 3;
+                       *u16++ = htons(D6_OPTION_FQDN);
+                       o->len = sizeof(*u16) * 4;
                }
                o->len = htons(o->len);
        }
diff --git a/dhcp6.h b/dhcp6.h
index 7189145d1b4f8f5cb56c6a2dbaff5715307159a1..de6e649932bf11f496a17368dfd80c70f90d65f0 100644 (file)
--- a/dhcp6.h
+++ b/dhcp6.h
@@ -79,6 +79,7 @@
 #define D6_OPTION_INFO_REFRESH_TIME    32
 #define D6_OPTION_BCMS_SERVER_D                33
 #define D6_OPTION_BCMS_SERVER_A                34
+#define D6_OPTION_FQDN                 39
 #define D6_OPTION_POSIX_TIMEZONE       41
 #define D6_OPTION_TZDB_TIMEZONE                42
 
index 35d7e867f70ceac2146cba96fd0240e2bdff0936..77ae5fce9a854524ffdb8b03ae4a52170e574c3f 100644 (file)
@@ -65,15 +65,27 @@ try_hostname()
 
 set_hostname()
 {
-       if need_hostname; then
-               if [ -n "$new_host_name" ]; then
+
+       need_hostname || return
+
+       if [ -n "$new_fqdn_name" ]; then
+               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"
-               elif [ -n "$new_fqdn_name" ]; then
-                       try_hostname "$new_fqdn_name"
                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
        set_hostname
 fi
index 3e943b06d8b8b99b74d02fbd3fc4b14edf9ad060..0a181b71f3a5d5d60f40fb95ddf93d47a05d6890 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 3, 2013
+.Dd June 5, 2013
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -604,8 +604,8 @@ running on the
 .Xr resolvconf 8
 .Sh STANDARDS
 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 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106.
+RFC 3633, RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702,
+RFC 4704, RFC 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106.
 .Sh AUTHORS
 .An Roy Marples Aq roy@marples.name
 .Sh BUGS
index ff698f5e7fac4b042e6b3ac881e327f7a5c0a5e9..4b0d5c0ca6667fe29fc9dd55987f77c207c7bfa4 100644 (file)
@@ -180,9 +180,11 @@ IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
 Only configure IPv4.
 .It Ic ipv6only
 Only confgiure IPv6.
-.It Ic fqdn Op none | ptr | both
-none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
+.It Ic fqdn Op disable | ptr | both
+ptr just asks the DHCP server to update the PTR
 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
 itself never does any DNS updates.
 .Nm dhcpcd
index c9f56ad5fe43639c422866c1f36d91c2cebeba25..509a93601dbaf577796df34b03d25227efee6c3e 100644 (file)
@@ -459,20 +459,20 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                add_environ(ifo, arg, 1);
                break;
        case 'h':
-               if (arg) {
-                       s = parse_string(ifo->hostname,
-                           HOSTNAME_MAX_LEN, arg);
-                       if (s == -1) {
-                               syslog(LOG_ERR, "hostname: %m");
-                               return -1;
-                       }
-                       if (s != 0 && ifo->hostname[0] == '.') {
-                               syslog(LOG_ERR,
-                                   "hostname cannot begin with .");
-                               return -1;
-                       }
-                       ifo->hostname[s] = '\0';
+               if (!arg) {
+                       ifo->options |= DHCPCD_HOSTNAME;
+                       break;
+               }
+               s = parse_string(ifo->hostname, HOSTNAME_MAX_LEN, arg);
+               if (s == -1) {
+                       syslog(LOG_ERR, "hostname: %m");
+                       return -1;
+               }
+               if (s != 0 && ifo->hostname[0] == '.') {
+                       syslog(LOG_ERR, "hostname cannot begin with .");
+                       return -1;
                }
+               ifo->hostname[s] = '\0';
                if (ifo->hostname[0] == '\0')
                        ifo->options &= ~DHCPCD_HOSTNAME;
                else
@@ -1131,12 +1131,6 @@ read_config(const char *file,
        ifo->reboot = DEFAULT_REBOOT;
        ifo->metric = -1;
        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);
        memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]);
index 1efb6e4787f37cc20c8faa2555d5289a808862e5..9cc91e7191e3851c67712b0cf494d1ad5fd1a95b 100644 (file)
@@ -45,7 +45,9 @@
 #define DEFAULT_TIMEOUT                30
 #define DEFAULT_REBOOT         5
 
+#ifndef HOSTNAME_MAX_LEN
 #define HOSTNAME_MAX_LEN       250     /* 255 - 3 (FQDN) - 2 (DNS enc) */
+#endif
 #define VENDORCLASSID_MAX_LEN  255
 #define CLIENTID_MAX_LEN       48
 #define USERCLASS_MAX_LEN      255