]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add DHCPv6 INFORM support.
authorRoy Marples <roy@marples.name>
Fri, 12 Oct 2012 10:31:51 +0000 (10:31 +0000)
committerRoy Marples <roy@marples.name>
Fri, 12 Oct 2012 10:31:51 +0000 (10:31 +0000)
This is automatically started when RA flags O is set.
If no DHCPv6 request options are configured in dhcpcd.conf then we
request domain servers and search lists.

18 files changed:
Makefile
configure.c
configure.h
defs.h
dhcp.c
dhcp.h
dhcp6.c [new file with mode: 0644]
dhcp6.h [new file with mode: 0644]
dhcpcd-hooks/20-resolv.conf
dhcpcd-run-hooks.8.in
dhcpcd-run-hooks.in
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
if-options.c
if-options.h
ipv6rs.c
net.c

index b5a7be7b2d457d94ae1034c1aaef380772e4be12..0a478804831dea659674ff85ca48646154d5776d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 PROG=          dhcpcd
 SRCS=          arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
 SRCS+=         configure.c if-options.c if-pref.c ipv4ll.c net.c signals.c
-SRCS+=         ipv6.c ipv6rs.c ipv6ns.c
+SRCS+=         ipv6.c ipv6rs.c ipv6ns.c dhcp6.c
 
 CFLAGS?=       -O2
 CSTD?=         c99
index 8003507acdfea1939ad976ca3fdd21943ab212d2..90df5e6b2f035d5f10b0b80482542f777f362a73 100644 (file)
@@ -44,6 +44,7 @@
 #include "common.h"
 #include "configure.h"
 #include "dhcp.h"
+#include "dhcp6.h"
 #include "if-options.h"
 #include "if-pref.h"
 #include "ipv6rs.h"
 
 static struct rt *routes;
 
+static const char *if_params[] = {
+       "interface",
+       "reason",
+       "pid",
+       "ifmetric",
+       "ifwireless",
+       "ifflags",
+       "ssid",
+       "profile",
+       "interface_order",
+       NULL
+};
+
+void
+if_printoptions(void)
+{
+       const char **p;
+
+       for (p = if_params; *p; p++)
+               printf(" -  %s\n", *p);
+}
+
 static int
 exec_script(char *const *argv, char *const *env)
 {
@@ -159,15 +182,21 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
        ssize_t e, elen, l;
        const struct if_options *ifo = iface->state->options;
        const struct interface *ifp;
-       int dhcp, ra;
+       int dhcp, dhcp6, ra;
+       const struct dhcp6_state *d6_state;
 
-       dhcp = ra = 0;
+       dhcp = dhcp6 = ra = 0;
+       d6_state = D6_STATE(iface);
        if (strcmp(reason, "TEST") == 0) {
-               if (ipv6rs_has_ra(iface))
+               if (d6_state && d6_state->new)
+                       dhcp6 = 1;
+               else if (ipv6rs_has_ra(iface))
                        ra = 1;
                else
                        dhcp = 1;
-       } else if (strcmp(reason, "ROUTERADVERT") == 0)
+       } else if (reason[strlen(reason) - 1] == '6')
+               dhcp6 = 1;
+       else if (strcmp(reason, "ROUTERADVERT") == 0)
                ra = 1;
        else
                dhcp = 1;
@@ -219,7 +248,10 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
        if (strcmp(reason, "TEST") == 0) {
                env[8] = strdup("if_up=false");
                env[9] = strdup("if_down=false");
-       } else if ((dhcp && iface->state->new) || (ra && ipv6rs_has_ra(iface))){
+       } else if ((dhcp && iface->state->new) ||
+           (dhcp6 && d6_state->new) ||
+           (ra && ipv6rs_has_ra(iface)))
+       {
                env[8] = strdup("if_up=true");
                env[9] = strdup("if_down=false");
        } else {
@@ -258,6 +290,15 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
                append_config(&env, &elen, "old",
                    (const char *const *)ifo->config);
        }
+       if (dhcp6 && d6_state->old) {
+               e = dhcp6_env(NULL, NULL, iface,
+                   d6_state->old, d6_state->old_len);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += dhcp6_env(env + elen, "old", iface,
+                           d6_state->old, d6_state->old_len);
+               }
+       }
 
 dumplease:
        if (dhcp && iface->state->new) {
@@ -270,6 +311,15 @@ dumplease:
                append_config(&env, &elen, "new",
                    (const char *const *)ifo->config);
        }
+       if (dhcp6 && d6_state->new) {
+               e = dhcp6_env(NULL, NULL, iface,
+                   d6_state->new, d6_state->new_len);
+               if (e > 0) {
+                       env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+                       elen += dhcp6_env(env + elen, "new", iface,
+                           d6_state->new, d6_state->new_len);
+               }
+       }
        if (ra) {
                e = ipv6rs_env(NULL, NULL, iface);
                if (e > 0) {
index e125369bdfe57bd805c0729874d68aa4049726b0..5e5090fa60fd91b4b1350ebd9f015c40ad114ae2 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "net.h"
 
+void if_printoptions(void);
 int send_interface(int, const struct interface *);
 int run_script_reason(const struct interface *, const char *);
 void build_routes(void);
diff --git a/defs.h b/defs.h
index 8f0f6deaec40b08004c84b1cef94dd9c51d0182e..dc92c6518910a3ea00c020f6a8ad44ee848d7a39 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -28,7 +28,7 @@
 #define CONFIG_H
 
 #define PACKAGE                        "dhcpcd"
-#define VERSION                        "5.6.2"
+#define VERSION                        "5.99.1"
 
 #ifndef CONFIG
 # define CONFIG                        SYSCONFDIR "/" PACKAGE ".conf"
diff --git a/dhcp.c b/dhcp.c
index df7c55a516189d6437757cbf3056ee88090a9763..e873d6a5618c20a8e1b8c62fb4de90ac1b134801 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
 #include "common.h"
 #include "dhcp.h"
 
-#define REQUEST        (1 << 0)
-#define UINT8  (1 << 1)
-#define UINT16 (1 << 2)
-#define SINT16 (1 << 3)
-#define UINT32 (1 << 4)
-#define SINT32 (1 << 5)
-#define IPV4   (1 << 6)
-#define STRING (1 << 7)
-#define PAIR   (1 << 8)
-#define ARRAY  (1 << 9)
-#define RFC3361        (1 << 10)
-#define RFC3397        (1 << 11)
-#define RFC3442 (1 << 12)
-#define RFC5969 (1 << 13)
-
-#define IPV4R  IPV4 | REQUEST
-
 #define DAD    "Duplicate address detected"
 
 /* Our aggregate option buffer.
  * practically never. See RFC3396 for details. */
 static uint8_t *opt_buffer;
 
-struct dhcp_opt {
-       uint8_t option;
-       int type;
-       const char *var;
-};
-
-static const struct dhcp_opt const dhcp_opts[] = {
+const struct dhcp_opt const dhcp_opts[] = {
        { 1,    IPV4 | REQUEST, "subnet_mask" },
                /* RFC 3442 states that the CSR has to come before all other
                 * routes. For completeness, we also specify static routes,
@@ -165,23 +142,10 @@ static const struct dhcp_opt const dhcp_opts[] = {
        { 0, 0, NULL }
 };
 
-static const char *if_params[] = {
-       "interface",
-       "reason",
-       "pid",
-       "ifmetric",
-       "ifwireless",
-       "ifflags",
-       "profile",
-       "interface_order",
-       NULL
-};
-
 static const char *dhcp_params[] = {
        "ip_address",
        "subnet_cidr",
        "network_number",
-       "ssid",
        "filename",
        "server_name",
        NULL
@@ -193,9 +157,6 @@ print_options(void)
        const struct dhcp_opt *opt;
        const char **p;
 
-       for (p = if_params; *p; p++)
-               printf(" -  %s\n", *p);
-
        for (p = dhcp_params; *p; p++)
                printf("    %s\n", *p);
 
@@ -204,7 +165,8 @@ print_options(void)
                        printf("%03d %s\n", opt->option, opt->var);
 }
 
-int make_option_mask(uint8_t *mask, const char *opts, int add)
+int make_option_mask(const struct dhcp_opt *dopts,
+    uint8_t *mask, const char *opts, int add)
 {
        char *token, *o, *p, *t;
        const struct dhcp_opt *opt;
@@ -214,7 +176,7 @@ int make_option_mask(uint8_t *mask, const char *opts, int add)
        while ((token = strsep(&p, ", "))) {
                if (*token == '\0')
                        continue;
-               for (opt = dhcp_opts; opt->option; opt++) {
+               for (opt = dopts; opt->option; opt++) {
                        if (!opt->var)
                                continue;
                        match = 0;
@@ -1177,7 +1139,7 @@ read_lease(const struct interface *iface)
        return dhcp;
 }
 
-static ssize_t
+ssize_t
 print_string(char *s, ssize_t len, int dl, const uint8_t *data)
 {
        uint8_t c;
@@ -1243,7 +1205,7 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
        return bytes;
 }
 
-static ssize_t
+ssize_t
 print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
 {
        const uint8_t *e, *t;
@@ -1254,7 +1216,8 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
        struct in_addr addr;
        ssize_t bytes = 0;
        ssize_t l;
-       char *tmp;
+       char *tmp, ntopbuf[INET6_ADDRSTRLEN];
+       const char *addr6;
 
        if (type & RFC3397) {
                l = decode_rfc3397(NULL, 0, dl, data);
@@ -1289,6 +1252,22 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
                return print_string(s, len, dl, data);
        }
 
+       /* DHCPv6 status code */
+       if (type & SCODE && dl >= (int)sizeof(u16)) {
+               if (s) {
+                       memcpy(&u16, data, sizeof(u16));
+                       u16 = ntohs(u16);
+                       l = snprintf(s, len, "%d ", u16);
+                       len -= l;
+               } else
+                       l = 7;
+               data += sizeof(u16);
+               dl -= sizeof(u16);
+               if (dl)
+                       l += print_option(s, len, STRING, dl, data);
+               return l;
+       }
+
        if (!s) {
                if (type & UINT8)
                        l = 3;
@@ -1307,6 +1286,11 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
                } else if (type & IPV4) {
                        l = 16;
                        dl /= 4;
+               } else if (type & IPV6) {
+                       l = INET6_ADDRSTRLEN;
+                       dl /= 16;
+               } else if (type & BINHEX) {
+                       l = 2;
                } else {
                        errno = EINVAL;
                        return -1;
@@ -1317,7 +1301,7 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
        t = data;
        e = data + dl;
        while (data < e) {
-               if (data != t) {
+               if (data != t && type != BINHEX) {
                        *s++ = ' ';
                        bytes++;
                        len--;
@@ -1349,6 +1333,14 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
                        memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
                        l = snprintf(s, len, "%s", inet_ntoa(addr));
                        data += sizeof(addr.s_addr);
+               } else if (type & IPV6) {
+                       addr6 = inet_ntop(AF_INET6, data,
+                           ntopbuf, sizeof(ntopbuf));
+                       l = snprintf(s, len, "%s", addr6);
+                       data += 16;
+               } else if (type & BINHEX) {
+                       l = snprintf(s, len, "%.2x", data[0]);
+                       data++; 
                } else
                        l = 0;
                len -= l;
diff --git a/dhcp.h b/dhcp.h
index fb27e71ba891566c42a6f1e074f06227b75d738d..e6190c89300091a90b623cc35bdd6223456e19b3 100644 (file)
--- a/dhcp.h
+++ b/dhcp.h
@@ -114,6 +114,26 @@ enum DHO {
        DHO_END                    = 255
 };
 
+#define REQUEST        (1 << 0)
+#define UINT8  (1 << 1)
+#define UINT16 (1 << 2)
+#define SINT16 (1 << 3)
+#define UINT32 (1 << 4)
+#define SINT32 (1 << 5)
+#define IPV4   (1 << 6)
+#define STRING (1 << 7)
+#define PAIR   (1 << 8)
+#define ARRAY  (1 << 9)
+#define RFC3361        (1 << 10)
+#define RFC3397        (1 << 11)
+#define RFC3442 (1 << 12)
+#define RFC5969 (1 << 13)
+#define IPV6   (1 << 14)
+#define BINHEX (1 << 15)
+#define SCODE  (1 << 16)
+
+#define IPV4R  IPV4 | REQUEST
+
 /* FQDN values - lsnybble used in flags
  * hsnybble to create order
  * and to allow 0x00 to mean disable
@@ -173,10 +193,18 @@ struct dhcp_lease {
 #include "if-options.h"
 #include "net.h"
 
+struct dhcp_opt {
+       uint16_t option;
+       int type;
+       const char *var;
+};
+
+extern const struct dhcp_opt const dhcp_opts[];
+
 #define add_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)))
-int make_option_mask(uint8_t *, const char *, int);
+int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
 void print_options(void);
 char *get_option_string(const struct dhcp_message *, uint8_t);
 int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
@@ -189,6 +217,9 @@ int get_option_uint8(uint8_t *, const struct dhcp_message *, uint8_t);
 struct rt *get_option_routes(const struct dhcp_message *, const char *,
     unsigned long long *);
 ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
+ssize_t print_string(char *s, ssize_t len, int dl, const uint8_t *data);
+ssize_t print_option(char *s, ssize_t len, int type, int dl,
+    const uint8_t *data);
 ssize_t configure_env(char **, const char *, const struct dhcp_message *,
     const struct if_options *);
 
diff --git a/dhcp6.c b/dhcp6.c
new file mode 100644 (file)
index 0000000..ae89d88
--- /dev/null
+++ b/dhcp6.c
@@ -0,0 +1,821 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+#ifdef __linux__
+#  define _LINUX_IN6_H
+#  include <linux/ipv6.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define ELOOP_QUEUE 2
+
+#include "config.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "duid.h"
+#include "eloop.h"
+#include "platform.h"
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+/*
+ * We presently don't have an IANA PEN, although I have applied for one.
+ * This means we can't really identify ourselves to a DHCPv6 server
+ * unlike our DHCPv4 stack.
+ * This blows chunks, but you maybe able to use your own or the
+ * IANA reserved one of 0.
+ */
+//#define DHCPCD_IANA_PEN 0
+
+/* Unsure if I want this */
+//#define VENDOR_SPLIT
+
+static int sock = -1;
+static struct sockaddr_in6 allrouters, from;
+static struct msghdr sndhdr;
+static struct iovec sndiov[2];
+static unsigned char *sndbuf;
+static struct msghdr rcvhdr;
+static struct iovec rcviov[2];
+static unsigned char *rcvbuf;
+static unsigned char ansbuf[1500];
+static unsigned char *duid;
+static uint16_t duid_len;
+static char ntopbuf[INET6_ADDRSTRLEN];
+
+struct dhcp6_op {
+       uint16_t type;
+       const char *name;
+};
+
+static const struct dhcp6_op dhcp6_ops[] = {
+       { DHCP6_SOLICIT, "SOLICIT6" },
+       { DHCP6_REQUEST, "REQUEST6" },
+       { DHCP6_REPLY, "REPLY6" },
+       { DHCP6_INFORMATION_REQ, "INFORM6" },
+       { 0, NULL }
+};
+
+const struct dhcp_opt const dhcp6_opts[] = {
+       { D6_OPTION_CLIENTID,           BINHEX,         "client_id" },
+       { D6_OPTION_SERVERID,           BINHEX,         "server_id" },
+       { D6_OPTION_IA_ADDR,            IPV6 | ARRAY,   "ia_addr" },
+       { D6_OPTION_PREFERENCE,         UINT8,          "preference" },
+       { D6_OPTION_RAPID_COMMIT,       0,              "rapid_commit" },
+       { D6_OPTION_UNICAST,            IPV6,           "unicast" },
+       { D6_OPTION_STATUS_CODE,        SCODE,          "status_code" },
+       { D6_OPTION_SIP_SERVERS_NAME,   RFC3397,        "sip_servers_names" },
+       { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6 | ARRAY,   "sip_servers_addresses" },
+       { D6_OPTION_DNS_SERVERS,        IPV6 | ARRAY,   "name_servers" },
+       { D6_OPTION_DOMAIN_LIST,        RFC3397,        "domain_search" },
+       { D6_OPTION_NIS_SERVERS,        IPV6 | ARRAY,   "nis_servers" },
+       { D6_OPTION_NISP_SERVERS,       IPV6 | ARRAY,   "nisp_servers" },
+       { D6_OPTION_NIS_DOMAIN_NAME,    RFC3397,        "nis_domain_name" },
+       { D6_OPTION_NISP_DOMAIN_NAME,   RFC3397,        "nisp_domain_name" },
+       { D6_OPTION_SNTP_SERVERS,       IPV6 | ARRAY,   "sntp_servers" },
+       { D6_OPTION_INFO_REFRESH_TIME,  UINT32,         "info_refresh_time" },
+       { D6_OPTION_BCMS_SERVER_D,      RFC3397,        "bcms_server_d" },
+       { D6_OPTION_BCMS_SERVER_A,      IPV6 | ARRAY,   "bcms_server_a" },
+       { 0, 0, NULL }
+};
+
+#if DEBUG_MEMORY
+static void
+dhcp6_cleanup(void)
+{
+
+       free(sndbuf);
+       free(rcvbuf);
+       free(duid);
+}
+#endif
+
+void
+dhcp6_printoptions(void)
+{
+       const struct dhcp_opt *opt;
+
+       for (opt = dhcp6_opts; opt->option; opt++)
+               if (opt->var)
+                       printf("%05d %s\n", opt->option, opt->var);
+}
+
+static int
+dhcp6_init(void)
+{
+       int len;
+
+#if DEBUG_MEMORY
+       atexit(dhcp6_cleanup);
+#endif
+
+       memset(&allrouters, 0, sizeof(allrouters));
+       allrouters.sin6_family = AF_INET6;
+       allrouters.sin6_port = htons(DHCP6_SERVER_PORT);
+#ifdef SIN6_LEN
+       allrouters.sin6_len = sizeof(allrouters);
+#endif
+       if (inet_pton(AF_INET6, ALLROUTERS, &allrouters.sin6_addr.s6_addr) != 1)
+               return -1;
+
+       len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+       sndbuf = calloc(1, len);
+       if (sndbuf == NULL)
+               return -1;
+       sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
+       sndhdr.msg_iov = sndiov;
+       sndhdr.msg_iovlen = 1;
+       sndhdr.msg_control = sndbuf;
+       sndhdr.msg_controllen = len;
+
+       rcvbuf = calloc(1, len);
+       if (rcvbuf == NULL) {
+               free(sndbuf);
+               sndbuf = NULL;
+               return -1;
+       }
+       rcvhdr.msg_name = &from;
+       rcvhdr.msg_namelen = sizeof(from);
+       rcvhdr.msg_iov = rcviov;
+       rcvhdr.msg_iovlen = 1;
+       rcvhdr.msg_control = rcvbuf;
+       rcvhdr.msg_controllen = len;
+       rcviov[0].iov_base = ansbuf;
+       rcviov[0].iov_len = sizeof(ansbuf);
+
+       return 0;
+}
+
+#ifdef DHCPCD_IANA_PEN
+static size_t
+dhcp6_makevendor(struct dhcp6_option *o)
+{
+       size_t len;
+       uint8_t *p;
+       uint16_t u16;
+       uint32_t u32;
+       size_t vlen;
+#ifdef VENDOR_SPLIT
+       const char *platform;
+       size_t plen, unl, url, uml, pl;
+       struct utsname utn;
+#endif
+
+       len = sizeof(uint32_t); /* IANA PEN */
+
+#ifdef VENDOR_SPLIT
+       plen = strlen(PACKAGE);
+       vlen = strlen(VERSION);
+       len += sizeof(uint16_t) + plen + 1 + vlen;
+       if (uname(&utn) == 0) {
+               unl = strlen(utn.sysname);
+               url = strlen(utn.release);
+               uml = strlen(utn.machine);
+               platform = hardware_platform();
+               pl = strlen(platform);
+               len += sizeof(uint16_t) + unl + 1 + url;
+               len += sizeof(uint16_t) + uml;
+               len += sizeof(uint16_t) + pl;
+       } else
+               unl = 0;
+#else
+       vlen = strlen(vendor);
+       len += sizeof(uint16_t) + vlen;
+#endif
+
+       if (o) {
+               o->code = htons(D6_OPTION_VENDOR);
+               o->len = htons(len);
+               p = D6_OPTION_DATA(o);
+               u32 = DHCPCD_IANA_PEN;
+               memcpy(p, &u32, sizeof(u32));
+               p += sizeof(u32);
+#ifdef VENDOR_SPLIT
+               u16 = htons(plen + 1 + vlen);
+               memcpy(p, &u16, sizeof(u16));
+               p += sizeof(u16);
+               memcpy(p, PACKAGE, plen);
+               p += plen;
+               *p++ = '-';
+               memcpy(p, VERSION, vlen);
+               p += vlen;
+               if (unl > 0) {
+                       u16 = htons(unl + 1 + url);
+                       memcpy(p, &u16, sizeof(u16));
+                       p += sizeof(u16);
+                       memcpy(p, utn.sysname, unl);
+                       p += unl;
+                       *p++ = '-';
+                       memcpy(p, utn.release, url);
+                       p += url;
+                       u16 = htons(uml);
+                       memcpy(p, &u16, sizeof(u16));
+                       p += sizeof(u16);
+                       memcpy(p, utn.machine, uml);
+                       p += uml;
+                       u16 = htons(pl);
+                       memcpy(p, &u16, sizeof(u16));
+                       p += sizeof(u16);
+                       memcpy(p, platform, pl);
+               }
+#else
+               u16 = htons(vlen);
+               memcpy(p, &u16, sizeof(u16));
+               p += sizeof(u16);
+               memcpy(p, vendor, vlen);
+#endif
+       }
+
+       return len;
+}
+#endif
+
+static const struct dhcp6_option *
+dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
+{
+       const struct dhcp6_option *o;
+
+       code = htons(code);
+       len -= sizeof(*m);
+       for (o = D6_CFIRST_OPTION(m);
+           len > (ssize_t)sizeof(*o);
+           o = D6_CNEXT_OPTION(o))
+       {
+               if (o->len == 0)
+                       break;
+               len -= sizeof(*o) + ntohs(o->len);
+               if (len < 0) {
+                       errno = EINVAL;
+                       return NULL;
+               }
+               if (o->code == code)
+                       return o;
+       }
+
+       errno = ESRCH;
+       return NULL;
+}
+
+
+static int
+dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
+{
+       struct dhcp6_state *state;
+       const struct dhcp6_option *co;
+       struct dhcp6_option *o;
+       time_t up;
+       uint16_t u16;
+
+       co = dhcp6_getoption(D6_OPTION_ELAPSED, m, len);
+       if (co == NULL)
+               return -1;
+
+       o = __UNCONST(co);
+       state = D6_STATE(ifp);
+       up = uptime() - state->start_uptime;
+       if (up < 0 || up > (time_t)UINT16_MAX)
+               up = (time_t)UINT16_MAX;
+       u16 = htons(up);
+       memcpy(D6_OPTION_DATA(o), &u16, sizeof(u16));
+       return 0;
+}
+
+static int
+dhcp6_makemessage(struct interface *ifp)
+{
+       struct dhcp6_state *state;
+       struct dhcp6_option *o;
+       int xid;
+       ssize_t len;
+       uint16_t *u16;
+       const struct if_options *ifo;
+       const struct dhcp_opt *opt;
+
+       state = D6_STATE(ifp);
+       if (state->send) {
+               free(state->send);
+               state->send = NULL;
+       }
+
+       /* Work out option size first */
+       ifo = ifp->state->options;
+       len = 0;
+       for (opt = dhcp6_opts; opt->option; opt++) {
+               if (!(opt->type & REQUEST ||
+                   has_option_mask(ifo->requestmask6, opt->option)))
+                       continue;
+               len += sizeof(*u16);
+       }
+       if (len == 0)
+               len = sizeof(*u16) * 2;
+       len += sizeof(*o);
+
+       len += sizeof(state->send);
+       len += sizeof(*o) + 14; /* clientid */ 
+       len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
+#ifdef DHCPCD_IANA_PEN
+       len += sizeof(*o) + dhcp6_makevendor(NULL);
+#endif
+
+       state->send = calloc(1, len);
+       if (state->send == NULL)
+               return -1;
+
+       state->send_len = len;
+       if (state->state == DH6S_INFORM)
+               state->send->type = DHCP6_INFORMATION_REQ;
+       else
+               state->send->type = DHCP6_SOLICIT;
+       xid = arc4random();
+       state->send->xid[0] = (xid >> 16) & 0xff;
+       state->send->xid[1] = (xid >> 8) & 0xff;
+       state->send->xid[2] = xid & 0xff;
+
+       o = D6_FIRST_OPTION(state->send);
+       o->code = htons(D6_OPTION_CLIENTID);
+       o->len = htons(duid_len);
+       memcpy(D6_OPTION_DATA(o), duid, duid_len);
+
+       o = D6_NEXT_OPTION(o);
+       o->code = htons(D6_OPTION_ELAPSED);
+       o->len = htons(sizeof(uint16_t));
+       dhcp6_updateelapsed(ifp, state->send, state->send_len);
+
+#ifdef DHCPCD_IANA_PEN
+       o = D6_NEXT_OPTION(o);
+       dhcp6_makevendor(o);
+#endif
+
+       o = D6_NEXT_OPTION(o);
+       o->code = htons(D6_OPTION_ORO);
+       o->len = 0;
+       u16 = (uint16_t *)(void *)D6_OPTION_DATA(o);
+       for (opt = dhcp6_opts; opt->option; opt++) {
+               if (!(opt->type & REQUEST ||
+                   has_option_mask(ifo->requestmask6, opt->option)))
+                       continue;
+               *u16++ = htons(opt->option);
+               o->len += sizeof(*u16);
+       }
+       if (o->len == 0) {
+               *u16++ = htons(D6_OPTION_DNS_SERVERS);
+               *u16++ = htons(D6_OPTION_DOMAIN_LIST);
+               o->len = sizeof(*u16) * 2;
+       }
+       o->len = htons(o->len);
+
+       return 0;
+}
+
+static const char *
+dhcp6_get_op(uint16_t type)
+{
+       const struct dhcp6_op *d;
+
+       for (d = dhcp6_ops; d->name; d++)
+               if (d->type == type)
+                       return d->name;
+       return NULL;
+}
+
+static void
+dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
+{
+       struct dhcp6_state *state;
+       struct timeval tv;
+       struct sockaddr_in6 to;
+       struct cmsghdr *cm;
+       struct in6_pktinfo pi;
+
+       state = D6_STATE(ifp);
+       if (!callback)
+               syslog(LOG_DEBUG, "%s: sending %s with xid 0x%02x%02x%02x",
+                   ifp->name,
+                   dhcp6_get_op(state->send->type),
+                   state->send->xid[0],
+                   state->send->xid[1],
+                   state->send->xid[2]);
+       else {
+               if (state->interval == 0)
+                       state->interval = 4;
+               else {
+                       state->interval *= 2;
+                       if (state->interval > 64)
+                               state->interval = 64;
+               }
+               tv.tv_sec = state->interval + DHCP_RAND_MIN;
+               tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+               syslog(LOG_DEBUG,
+                   "%s: sending %s (xid 0x%02x%02x%02x), next in %0.2f seconds",
+                   ifp->name, dhcp6_get_op(state->send->type),
+                   state->send->xid[0],
+                   state->send->xid[1],
+                   state->send->xid[2],
+                   timeval_to_double(&tv));
+       }
+
+       to = allrouters;
+       sndhdr.msg_name = (caddr_t)&to;
+       sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
+       sndhdr.msg_iov[0].iov_len = state->send_len;
+
+       /* Set the outbound interface */
+       cm = CMSG_FIRSTHDR(&sndhdr);
+       cm->cmsg_level = IPPROTO_IPV6;
+       cm->cmsg_type = IPV6_PKTINFO;
+       cm->cmsg_len = CMSG_LEN(sizeof(pi));
+       memset(&pi, 0, sizeof(pi));
+       pi.ipi6_ifindex = ifp->index;
+       memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
+       
+       if (sendmsg(sock, &sndhdr, 0) == -1)
+               syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
+
+       if (callback)
+               add_timeout_tv(&tv, callback, ifp);
+}
+
+static void
+dhcp6_sendinform(void *arg)
+{
+
+       dhcp6_sendmessage(arg, dhcp6_sendinform);
+}
+
+/* ARGSUSED */
+static void
+dhcp6_handledata(_unused void *arg)
+{
+       ssize_t len;
+       struct cmsghdr *cm;
+       struct in6_pktinfo pkt;
+       struct interface *ifp;
+       const char *sfrom, *op;
+       struct dhcp6_message *m, *r;
+       struct dhcp6_state *state;
+       const struct dhcp6_option *o;
+       const char *reason;
+       const struct dhcp_opt *opt;
+       const struct if_options *ifo;
+
+       len = recvmsg(sock, &rcvhdr, 0);
+       if (len == -1) {
+               syslog(LOG_ERR, "recvmsg: %m");
+               return;
+       }
+       sfrom = inet_ntop(AF_INET6, &from.sin6_addr,
+           ntopbuf, INET6_ADDRSTRLEN);
+       if ((size_t)len < sizeof(struct dhcp6_message)) {
+               syslog(LOG_ERR, "DHCPv6 RA packet too short from %s", sfrom);
+               return;
+       }
+
+       pkt.ipi6_ifindex = 0;
+       for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr);
+            cm;
+            cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm))
+       {
+               if (cm->cmsg_level != IPPROTO_IPV6)
+                       continue;
+               switch(cm->cmsg_type) {
+               case IPV6_PKTINFO:
+                       if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
+                               memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
+                       break;
+               }
+       }
+
+       if (pkt.ipi6_ifindex == 0) {
+               syslog(LOG_ERR,
+                   "DHCPv6 reply did not contain index from %s",
+                   sfrom);
+               return;
+       }
+
+
+       for (ifp = ifaces; ifp; ifp = ifp->next)
+               if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
+                       break;
+       if (ifp == NULL) {
+               syslog(LOG_ERR, "DHCPv6 reply for unexpected interface from %s",
+                   sfrom);
+               return;
+       }
+       state = D6_STATE(ifp);
+       if (state == NULL || state->send == NULL) {
+               syslog(LOG_ERR, "%s: DHCPv6 reply received but not running",
+                   ifp->name);
+               return;
+       }
+
+       m = state->send;
+       r = (struct dhcp6_message *)rcvhdr.msg_iov[0].iov_base;
+       if (r->xid[0] != m->xid[0] ||
+           r->xid[1] != m->xid[1] ||
+           r->xid[2] != m->xid[2])
+       {
+               syslog(LOG_ERR,
+                   "%s: wrong xid 0x%02x%02x%02x (expecting 0x%02x%02x%02x) from %s",
+                   ifp->name,
+                   r->xid[0], r->xid[1], r->xid[2],
+                   r->xid[0], r->xid[1], r->xid[2],
+                   sfrom);
+               return;
+       }
+
+       if (dhcp6_getoption(D6_OPTION_SERVERID, r, len) == NULL) {
+               syslog(LOG_ERR, "%s: no DHCPv6 server ID from %s",
+                   ifp->name, sfrom);
+               return;
+       }
+
+       o = dhcp6_getoption(D6_OPTION_CLIENTID, r, len);
+       if (o && ntohs(o->len) != duid_len &&
+           memcmp(D6_COPTION_DATA(o), duid, duid_len) != 0)
+       {
+               syslog(LOG_ERR, "%s: incorrect client ID from %s",
+                   ifp->name, sfrom);
+               return;
+       }
+
+       ifo = ifp->state->options;
+       for (opt = dhcp6_opts; opt->option; opt++) {
+               if (has_option_mask(ifo->requiremask6, opt->option) &&
+                   dhcp6_getoption(opt->option, r, len) == NULL)
+               {
+                       syslog(LOG_WARNING,
+                           "%s: reject DHCPv6 (no option %s) from %s",
+                           ifp->name, opt->var, sfrom);
+                       return;
+               }
+       }
+
+       m = malloc(len);
+       if (m == NULL) {
+               syslog(LOG_ERR, "%s: malloc DHCPv6 reply: %m", ifp->name);
+               return;
+       }
+
+       free(state->old);
+       state->old = state->new;
+       state->old_len = state->new_len;
+       state->new = malloc(len);
+       state->new = m;
+       memcpy(m, r, len);
+       state->new_len = len;
+
+       op = dhcp6_get_op(r->type);
+       if (r->type != DHCP6_REPLY) {
+               syslog(LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
+                   ifp->name, op, r->type);
+               return;
+       }
+
+       syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
+       switch(state->state) {
+       case DH6S_INFORM:
+               reason = "INFORM6";
+               break;
+       default:
+               reason = "UNKNOWN6";
+               break;
+       }
+       run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
+       if (options & DHCPCD_TEST ||
+           (ifp->state->options->options & DHCPCD_INFORM &&
+           !(options & DHCPCD_MASTER)))
+       {
+#ifdef DEBUG_MEMORY
+               dhcp6_free(ifp);
+#endif
+               exit(EXIT_SUCCESS);
+       }
+       delete_timeout(NULL, ifp);
+}
+
+
+static int
+dhcp6_open(void)
+{
+       struct sockaddr_in6 sa;
+       int n;
+
+       if (sndbuf == NULL && dhcp6_init() == -1)
+               return -1;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sin6_family = AF_INET6;
+       sa.sin6_port = htons(DHCP6_CLIENT_PORT);
+#ifdef BSD
+       sa.sin6_len = sizeof(sa);
+#endif
+
+       sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+       if (sock == -1)
+               return -1;
+
+       n = 1;
+       if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+           &n, sizeof(n)) == -1)
+               goto errexit;
+
+       n = 1;
+       if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+           &n, sizeof(n)) == -1)
+               goto errexit;
+
+#ifdef SO_REUSEPORT
+       n = 1;
+       if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+           &n, sizeof(n)) == -1)
+               goto errexit;
+#endif
+
+       if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1)
+               goto errexit;
+
+       n = 1;
+       if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+           &n, sizeof(n)) == -1)
+               goto errexit;
+
+       if (set_cloexec(sock) == -1 || set_nonblock(sock) == -1)
+               goto errexit;
+
+       add_event(sock, dhcp6_handledata, NULL);
+
+       return 0;
+
+errexit:
+       close(sock);
+       return -1;
+}
+
+int
+dhcp6_start(struct interface *ifp, int manage)
+{
+       struct dhcp6_state *state;
+
+       state = D6_STATE(ifp);
+       if (state) {
+               /* We're already running DHCP6 */
+               /* XXX: What if the managed flag changes? */
+               return 0;
+       }
+
+       syslog(LOG_INFO, "%s: %s", ifp->name,
+           manage ? "soliciting DHCPv6 address" :
+           "requesting DHCPv6 information");
+
+       if (sock == -1 && dhcp6_open() == -1)
+               return -1;
+
+       if (duid == NULL) {
+               duid = malloc(DUID_LEN);
+               if (duid == NULL)
+                       return -1;
+               duid_len = get_duid(duid, ifp);
+       }
+
+       ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+       state = D6_STATE(ifp);
+       if (state == NULL)
+               return -1;
+
+       state->state = manage ? DH6S_INIT : DH6S_INFORM;
+       state->start_uptime = uptime();
+
+       if (dhcp6_makemessage(ifp) == -1)
+               return -1;
+
+       if (state->state == DH6S_INFORM)
+               dhcp6_sendinform(ifp);
+
+       return 1;
+}
+
+static void
+dhcp6_freedrop(struct interface *ifp, int drop)
+{
+       struct dhcp6_state *state;
+
+       delete_timeout(NULL, ifp);
+       state = D6_STATE(ifp);
+       if (state) {
+               if (drop && state->new)
+                       run_script_reason(ifp, "STOP6");
+               free(state->send);
+               free(state->new);
+               free(state->old);
+               free(state);
+               ifp->if_data[IF_DATA_DHCP6] = NULL;
+       }
+
+       /* If we don't have any more DHCP6 enabled interfaces,
+        * close the global socket */
+       for (ifp = ifaces; ifp; ifp = ifp->next)
+               if (D6_STATE(ifp))
+                       break;
+       if (ifp == NULL && sock != -1) {
+               close(sock);
+               delete_event(sock);
+               sock = -1;
+       }
+}
+
+void
+dhcp6_drop(struct interface *ifp)
+{
+
+       dhcp6_freedrop(ifp, 1);
+}
+
+void
+dhcp6_free(struct interface *ifp)
+{
+
+       dhcp6_freedrop(ifp, 0);
+}
+
+ssize_t
+dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+    const struct dhcp6_message *m, ssize_t mlen)
+{
+       const struct if_options *ifo;
+       const struct dhcp_opt *opt;
+       const struct dhcp6_option *o;
+       ssize_t len, e;
+       uint16_t ol;
+       const uint8_t *od;
+       char **ep, *v, *val;
+
+       e = 0;
+       ep = env;
+       ifo = ifp->state->options;
+       for (opt = dhcp6_opts; opt->option; opt++) {
+               if (!opt->var)
+                       continue;
+               if (has_option_mask(ifo->nomask6, opt->option))
+                       continue;
+               o = dhcp6_getoption(opt->option, m, mlen);
+               if (o == NULL)
+                       continue;
+               if (env == NULL) {
+                       e++;
+                       continue;
+               }
+               ol = ntohs(o->len);
+               od = D6_COPTION_DATA(o);
+               len = print_option(NULL, 0, opt->type, ol, od);
+               if (len < 0)
+                       return -1;
+               e = strlen(prefix) + 6 + strlen(opt->var) + len + 4;
+               v = val = *ep++ = xmalloc(e);
+               v += snprintf(val, e, "%s_dhcp6_%s=", prefix, opt->var);
+               if (len != 0)
+                       print_option(v, len, opt->type, ol, od);
+
+       }
+
+       if (env == NULL)
+               return e;
+       return ep - env;
+}
diff --git a/dhcp6.h b/dhcp6.h
new file mode 100644 (file)
index 0000000..a42f532
--- /dev/null
+++ b/dhcp6.h
@@ -0,0 +1,140 @@
+/* 
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DHCP6_H
+#define DHCP6_H
+
+#include "dhcpcd.h"
+
+/* UDP port numbers for DHCP */
+#define DHCP6_CLIENT_PORT      546
+#define DHCP6_SERVER_PORT      547
+
+/* DHCP message type */
+#define DHCP6_SOLICIT          1
+#define DHCP6_ADVERTISE                2
+#define DHCP6_REQUEST          3
+#define DHCP6_CONFIRM          4
+#define DHCP6_RENEW            5
+#define DHCP6_REBIND           6
+#define DHCP6_REPLY            7
+#define DHCP6_RELEASE          8
+#define DHCP6_DECLINE          9
+#define DHCP6_RECONFIGURE      10
+#define DHCP6_INFORMATION_REQ  11
+#define DHCP6_RELAY_FLOW       12
+#define DHCP6_RELAY_REPL       13
+
+#define D6_OPTION_CLIENTID             1
+#define D6_OPTION_SERVERID             2
+#define D6_OPTION_ORO                  6
+#define D6_OPTION_IA_ADDR              5
+#define D6_OPTION_PREFERENCE           7
+#define D6_OPTION_ELAPSED              8
+#define D6_OPTION_RAPID_COMMIT         9
+#define D6_OPTION_UNICAST              12
+#define D6_OPTION_STATUS_CODE          13
+#define D6_OPTION_VENDOR               16
+#define D6_OPTION_SIP_SERVERS_NAME     21
+#define D6_OPTION_SIP_SERVERS_ADDRESS  22
+#define D6_OPTION_DNS_SERVERS          23
+#define D6_OPTION_DOMAIN_LIST          24
+#define D6_OPTION_NIS_SERVERS          27
+#define D6_OPTION_NISP_SERVERS         28
+#define D6_OPTION_NIS_DOMAIN_NAME      29
+#define D6_OPTION_NISP_DOMAIN_NAME     30
+#define D6_OPTION_SNTP_SERVERS         31
+#define D6_OPTION_INFO_REFRESH_TIME    32
+#define D6_OPTION_BCMS_SERVER_D                33
+#define D6_OPTION_BCMS_SERVER_A                34
+
+#include "dhcp.h"
+extern const struct dhcp_opt const dhcp6_opts[];
+
+struct dhcp6_message {
+       uint8_t type;
+       uint8_t xid[3];
+       /* followed by options */
+} _packed;
+
+struct dhcp6_option {
+       uint16_t code;
+       uint16_t len;
+       /* followed by data */
+} _packed;
+
+enum DH6S {
+       DH6S_INIT,
+       DH6S_DISCOVER,
+       DH6S_REQUEST,
+       DH6S_BOUND,
+       DH6S_RENEW,
+       DH6S_REBIND,
+       DH6S_REBOOT,
+       DH6S_INFORM,
+       DH6S_RENEW_REQUESTED,
+       DH6S_PROBE
+};
+
+struct dhcp6_state {
+       enum DH6S state;
+       time_t start_uptime;
+       int interval;
+       struct dhcp6_message *send;
+       size_t send_len;
+       struct dhcp6_message *new;
+       size_t new_len;
+       struct dhcp6_message *old;
+       size_t old_len;
+};
+
+#define D6_STATE(ifp) ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_FIRST_OPTION(m)                                                    \
+    ((struct dhcp6_option *)                                                  \
+        ((uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_NEXT_OPTION(o)                                                     \
+    ((struct dhcp6_option *)                                                  \
+        (((uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_OPTION_DATA(o)                                                     \
+    ((uint8_t *)(o) + sizeof(struct dhcp6_option))
+#define D6_CFIRST_OPTION(m)                                                   \
+    ((const struct dhcp6_option *)                                            \
+        ((const uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_CNEXT_OPTION(o)                                                    \
+    ((const struct dhcp6_option *)                                            \
+        (((const uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_COPTION_DATA(o)                                                    \
+    ((const uint8_t *)(o) + sizeof(struct dhcp6_option))
+
+void dhcp6_printoptions(void);
+int dhcp6_start(struct interface *, int);
+ssize_t dhcp6_env(char **, const char *, const struct interface *,
+    const struct dhcp6_message *, ssize_t);
+void dhcp6_free(struct interface *);
+void dhcp6_drop(struct interface *);
+
+#endif
index 42aff68bb977ed2bee16d9fb0f829708879e61c3..719202de30c10f8b786b1d84294c8d6daf057011 100644 (file)
@@ -138,6 +138,14 @@ remove_resolv_conf()
        fi
 }
 
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+INFORM6)
+       new_domain_name_servers="$new_dhcp6_name_servers"
+       new_domain_search="$new_dhcp6_domain_search"
+       ;;
+esac
+
 if $if_up || [ "$reason" = ROUTERADVERT ]; then
        add_resolv_conf
 elif $if_down; then
index c0dc081525174fd672c339f17b79211694619fee..789171aa7cdd2685b1e3e4e88474b44f74ee1c52 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 19, 2012
+.Dd October 11, 2012
 .Dt DHCPCD-RUN-HOOKS 8
 .Os
 .Sh NAME
@@ -75,7 +75,7 @@ dhcpcd is starting up and any pre-initialisation should be done.
 .It Dv CARRIER
 dhcpcd has detected the carrier is up.
 This is generally just a notification and no action need be taken.
-.It Dv INFORM
+.It Dv INFORM | Dv INFORM6
 dhcpcd informed a DHCP server about it's address and obtained other
 configuration details.
 .It Dv BOUND
@@ -110,7 +110,7 @@ dhcpcd failed to operate on the interface.
 This normally happens when dhcpcd does not support the raw interface, which
 means it cannot work as a DHCP or ZeroConf client.
 Static configuration and DHCP INFORM is still allowed.
-.It Dv STOP
+.It Dv STOP | Dv STOP6
 dhcpcd stopped running on the interface.
 .It Dv DUMP
 dhcpcd has been asked to dump the last lease for the interface.
index 55a8f4c3b67025b90b544c23cc85af21b2c8447e..7f46cd623d6c101f25501d1a0786fc0564d6c44d 100644 (file)
@@ -2,11 +2,14 @@
 # dhcpcd client configuration script 
 
 # Handy variables and functions for our hooks to use
-if [ "$reason" = ROUTERADVERT ]; then
-       ifsuffix=":ra"
-else
-       ifsuffix=
-fi
+case "$reason" in
+       ROUTERADVERT)
+               ifsuffix=":ra";;
+       INFORM6|BOUND6|RENEW6|REBIND6|EXPIRE6|RELEASE6|STOP6)
+               ifsuffix=":dhcp6";;
+       *)
+               ifsuffix=;;
+esac
 ifname="$interface$ifsuffix"
 
 from=from
index d41f6245e5b135eb14b283dfd25d020e9052a380..22197c2cd5786f0180cd61eb1b8b82f6eeab2e3b 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 7, 2012
+.Dd October 11, 2012
 .Dt DHCPCD 8
 .Os
 .Sh NAME
 .Nm dhcpcd
-.Nd an RFC 2131 compliant DHCP client
+.Nd a DHCP client
 .Sh SYNOPSIS
 .Nm
-.Op Fl ABbDdEGgHJKkLnpqTVw
+.Op Fl 46ABbDdEGgHJKkLnpqTVw
 .Op Fl C , Fl Fl nohook Ar hook
 .Op Fl c , Fl Fl script Ar script
 .Op Fl e , Fl Fl env Ar value
@@ -114,6 +114,12 @@ is managing routes,
 .Nm
 sends Neighbor Solicitions to each advertising router periodically and will
 expire the ones that do not respond.
+.Pp
+.Nm
+is also an implemenation of a DHCPv6 client as specified in
+.Li RFC 3315
+but only for sending Information Request messages when instructed to do so
+by an IPv6 Router Advertisment or manually.
 .Ss Local Link configuration
 If
 .Nm
@@ -424,6 +430,10 @@ However, there are sometimes situations where you don't want the things to be
 configured exactly how the the DHCP server wants.
 Here are some options that deal with turning these bits off.
 .Bl -tag -width indent
+.It Fl 4 , Fl Fl ipv4only
+Only configure IPv4.
+.It Fl 6 , Fl Fl ipv6only
+Only confgiure IPv6.
 .It Fl A , Fl Fl noarp
 Don't request or claim the address by ARP.
 This also disables IPv4LL.
@@ -591,9 +601,9 @@ running on the
 .Xr dhcpcd-run-hooks 8 ,
 .Xr resolvconf 8
 .Sh STANDARDS
-RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
-RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861, RFC 5969,
-RFC 6106.
+RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361,
+RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861,
+RFC 5969, RFC 6106.
 .Sh AUTHORS
 .An Roy Marples Aq roy@marples.name
 .Sh BUGS
index 6933b9faabace86d531c33d081646356682e530c..b124d1f5c4393784c1c531e371501b5ca5a31b07 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -63,6 +63,7 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
 #include "configure.h"
 #include "control.h"
 #include "dhcpcd.h"
+#include "dhcp6.h"
 #include "duid.h"
 #include "eloop.h"
 #include "if-options.h"
@@ -150,7 +151,7 @@ static void
 usage(void)
 {
 
-printf("usage: "PACKAGE"\t[-ABbDdEGgHJKkLnpqTVw]\n"
+printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnpqTVw]\n"
        "\t\t[-C, --nohook hook] [-c, --script script]\n"
        "\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n"
        "\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n"
@@ -269,6 +270,7 @@ stop_interface(struct interface *iface)
        else
                ifaces = ifp->next;
 
+       dhcp6_drop(iface);
        ipv6rs_drop(iface);
        if (strcmp(iface->state->reason, "RELEASE") != 0)
                drop_dhcp(iface, "STOP");
@@ -818,6 +820,9 @@ configure_interface1(struct interface *iface)
 
        free(iface->clientid);
        iface->clientid = NULL;
+       if (!(ifo->options & DHCPCD_IPV4))
+               return;
+
        if (*ifo->clientid) {
                iface->clientid = xmalloc(ifo->clientid[0] + 1);
                memcpy(iface->clientid, ifo->clientid, ifo->clientid[0] + 1);
@@ -1185,7 +1190,8 @@ start_interface(void *arg)
        free(iface->state->offer);
        iface->state->offer = NULL;
 
-       if (options & DHCPCD_IPV6RS && ifo->options & DHCPCD_IPV6RS)
+       if (options & DHCPCD_IPV6RS && ifo->options & DHCPCD_IPV6RS &&
+           !(ifo->options & DHCPCD_INFORM))
                ipv6rs_start(iface);
 
        if (iface->state->arping_index < ifo->arping_len) {
@@ -1196,6 +1202,13 @@ start_interface(void *arg)
                start_static(iface);
                return;
        }
+
+       if (ifo->options & DHCPCD_INFORM && ifo->options & DHCPCD_IPV6)
+               dhcp6_start(iface, 0);
+
+       if (!(ifo->options & DHCPCD_IPV4))
+               return;
+
        if (ifo->options & DHCPCD_INFORM) {
                start_inform(iface);
                return;
@@ -1771,7 +1784,7 @@ int
 main(int argc, char **argv)
 {
        struct interface *iface;
-       int opt, oi = 0, signal_fd, sig = 0, i, control_fd;
+       int opt, oi = 0, signal_fd, sig = 0, i, control_fd, family = 0;
        size_t len;
        pid_t pid;
        struct timespec ts;
@@ -1807,6 +1820,12 @@ main(int argc, char **argv)
        while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
        {
                switch (opt) {
+               case '4':
+                       family = AF_INET;
+                       break;
+               case '6':
+                       family = AF_INET6;
+                       break;
                case 'f':
                        cffile = optarg;
                        break;
@@ -1829,7 +1848,16 @@ main(int argc, char **argv)
                        i = 2;
                        break;
                case 'V':
-                       print_options();
+                       printf("Interface options:\n");
+                       if_printoptions();
+                       if (family == 0 || family == AF_INET) {
+                               printf("\nDHCPv4 options:\n");
+                               print_options();
+                       }
+                       if (family == 0 || family == AF_INET6) {
+                               printf("\nDHCPv6 options:\n");
+                               dhcp6_printoptions();
+                       }
                        exit(EXIT_SUCCESS);
                case '?':
                        usage();
index a1decb747223ee291208f7a0eb42eef39297ca5e..a8908f7604dd399b7e56f1e22cd9a9e2d08fe9ae 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd May 21, 2012
+.Dd October 11, 2012
 .Dt DHCPCD.CONF 5 SMM
 .Os
 .Sh NAME
@@ -133,6 +133,10 @@ is an empty string then the current system hostname is sent.
 If
 .Ar hostname
 is a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic ipv4only
+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
 record of the host in DNS whereas both also updates the A record.
@@ -154,15 +158,15 @@ RDNSS option.
 Set this option so to make
 .Nm dhcpcd
 always fork on an RA.
-.It ic ipv6ra_own
+.It Ic ipv6ra_own
 Disables kernel IPv6 Router Advertisment processing so dhcpcd can manage
 addresses and routes.
-.It ic ipv6ra_own_default
+.It Ic ipv6ra_own_default
 Each time dhcpcd receives an IPv6 Router Adveristment, dhcpcd will manage
 the default route only.
 This allows dhcpcd to prefer an interface for outbound traffic based on metric
 and/or user selection rather than the kernel.
-.It ic ipv6rs
+.It Ic ipv6rs
 Enables IPv6 Router Advertisment solicitation.
 This is on by default, but is documented here in the case where it is disabled
 globally but needs to be enabled for one interface.
index 5b517979ae508423b083ca1ddb561d4077749cde..75cac3f1ed54cdf4b10f7e5a1e93b5e393d5a8d2 100644 (file)
@@ -42,6 +42,8 @@
 
 #include "config.h"
 #include "common.h"
+#include "dhcp.h"
+#include "dhcp6.h"
 #include "if-options.h"
 #include "net.h"
 #include "platform.h"
@@ -114,6 +116,8 @@ const struct option cf_options[] = {
        {"ipv6ra_fork",     no_argument,       NULL, O_IPV6RA_FORK},
        {"ipv6ra_own",      no_argument,       NULL, O_IPV6RA_OWN},
        {"ipv6ra_own_default", no_argument,    NULL, O_IPV6RA_OWN_D},
+       {"ipv4only",        no_argument,       NULL, '4'},
+       {"ipv6only",        no_argument,       NULL, '6'},
        {NULL,              0,                 NULL, '\0'}
 };
 
@@ -334,6 +338,26 @@ parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
        return 0;
 }
 
+static const char * 
+set_option_space(const char *arg, const struct dhcp_opt **d,
+    struct if_options *ifo,
+    uint8_t *request[], uint8_t *require[], uint8_t *no[])
+{
+
+       if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
+               *d = dhcp6_opts;
+               *request = ifo->requestmask6;
+               *require = ifo->requiremask6;
+               *no = ifo->nomask6;
+               return arg + strlen("dhcp6_");
+       }
+       *d = dhcp_opts;
+       *request = ifo->requestmask;
+       *require = ifo->requiremask;
+       *no = ifo->nomask;
+       return arg;
+}
+
 static int
 parse_option(struct if_options *ifo, int opt, const char *arg)
 {
@@ -342,6 +366,8 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        ssize_t s;
        struct in_addr addr, addr2;
        struct rt *rt;
+       const struct dhcp_opt const *d;
+       uint8_t *request, *require, *no;
 
        switch(opt) {
        case 'f': /* FALLTHROUGH */
@@ -419,7 +445,8 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                }
                break;
        case 'o':
-               if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
+               arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+               if (make_option_mask(d, request, arg, 1) != 0) {
                        syslog(LOG_ERR, "unknown option `%s'", arg);
                        return -1;
                }
@@ -437,6 +464,12 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                ifo->req_mask.s_addr = 0;
                break;
        case 's':
+               if (ifo->options & DHCPCD_IPV6 &&
+                   !(ifo->options & DHCPCD_IPV4))
+               {
+                       ifo->options |= DHCPCD_INFORM;
+                       break;
+               }
                if (arg && *arg != '\0') {
                        if (parse_addr(&ifo->req_addr, &ifo->req_mask,
                                arg) != 0)
@@ -612,17 +645,19 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                ifo->options &= ~DHCPCD_IPV4LL;
                break;
        case 'O':
-               if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
-                   make_option_mask(ifo->requiremask, arg, -1) != 0 ||
-                   make_option_mask(ifo->nomask, arg, 1) != 0)
+               arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+               if (make_option_mask(d, request, arg, -1) != 0 ||
+                   make_option_mask(d, require, arg, -1) != 0 ||
+                   make_option_mask(d, no, arg, 1) != 0)
                {
                        syslog(LOG_ERR, "unknown option `%s'", arg);
                        return -1;
                }
                break;
        case 'Q':
-               if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
-                   make_option_mask(ifo->requestmask, arg, 1) != 0)
+               arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+               if (make_option_mask(d, require, arg, 1) != 0 ||
+                   make_option_mask(d, request, arg, 1) != 0)
                {
                        syslog(LOG_ERR, "unknown option `%s'", arg);
                        return -1;
@@ -730,6 +765,14 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
        case 'Z':
                ifdv = splitv(&ifdc, ifdv, arg);
                break;
+       case '4':
+               ifo->options &= ~(DHCPCD_IPV6 | DHCPCD_IPV6RS);
+               ifo->options |= DHCPCD_IPV4;
+               break;
+       case '6':
+               ifo->options &= ~DHCPCD_IPV4;
+               ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS;
+               break;
        case O_ARPING:
                if (parse_addr(&addr, NULL, arg) != 0)
                        return -1;
@@ -738,7 +781,7 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
                ifo->arping[ifo->arping_len++] = addr.s_addr;
                break;
        case O_DESTINATION:
-               if (make_option_mask(ifo->dstmask, arg, 2) != 0) {
+               if (make_option_mask(dhcp_opts, ifo->dstmask, arg, 2) != 0) {
                        if (errno == EINVAL)
                                syslog(LOG_ERR, "option `%s' does not take"
                                    " an IPv4 address", arg);
@@ -808,9 +851,10 @@ read_config(const char *file,
 
        /* Seed our default options */
        ifo = xzalloc(sizeof(*ifo));
+       ifo->options |= DHCPCD_IPV4 | DHCPCD_IPV4LL;
        ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE | DHCPCD_LINK;
-       ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL;
-       ifo->options |= DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
+       ifo->options |= DHCPCD_ARP;
+       ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
        ifo->timeout = DEFAULT_TIMEOUT;
        ifo->reboot = DEFAULT_REBOOT;
        ifo->metric = -1;
index 212cd8d40e74fe33c075d4d565767074545295a0..f35e8f0f17f62555ddc533f1192f79637d903caf 100644 (file)
 
 #include <getopt.h>
 #include <limits.h>
+#include <stdint.h>
 
 /* Don't set any optional arguments here so we retain POSIX
  * compatibility with getopt */
-#define IF_OPTS "bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
+#define IF_OPTS "46bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
 
 #define DEFAULT_TIMEOUT                30
 #define DEFAULT_REBOOT         5
@@ -83,6 +84,7 @@
 #define DHCPCD_IPV6RA_OWN_DEFAULT      (1ULL << 34)
 #define DHCPCD_IPV4                    (1ULL << 35)
 #define DHCPCD_FORKED                  (1ULL << 36)
+#define DHCPCD_IPV6                    (1ULL << 37)
 
 extern const struct option cf_options[];
 
@@ -91,6 +93,9 @@ struct if_options {
        uint8_t requestmask[256 / 8];
        uint8_t requiremask[256 / 8];
        uint8_t nomask[256 / 8];
+       uint8_t requestmask6[(UINT16_MAX + 1) / 8];
+       uint8_t requiremask6[(UINT16_MAX + 1) / 8];
+       uint8_t nomask6[(UINT16_MAX + 1) / 8];
        uint8_t dstmask[256 / 8];
        uint32_t leasetime;
        time_t timeout;
index 6c79e9beee6f913c3b34f3b481440e71d89ff05a..211f1abafe188301ff39d608a2c004e351919051 100644 (file)
--- a/ipv6rs.c
+++ b/ipv6rs.c
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
 
+#ifdef __linux__
+#  define _LINUX_IN6_H
+#  include <linux/ipv6.h>
+#endif
+
 #include <errno.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
 
-#ifdef __linux__
-#  define _LINUX_IN6_H
-#  include <linux/ipv6.h>
-#endif
-
 #define ELOOP_QUEUE 1
 #include "bind.h"
 #include "common.h"
 #include "configure.h"
 #include "dhcpcd.h"
+#include "dhcp6.h"
 #include "eloop.h"
 #include "ipv6.h"
 #include "ipv6ns.h"
 #include "ipv6rs.h"
 
+/* Debugging Router Solicitations is a lot of spam, so disable it */
+//#define DEBUG_RS
+
 #define RTR_SOLICITATION_INTERVAL       4 /* seconds */
 #define MAX_RTR_SOLICITATIONS           3 /* times */
 
@@ -379,9 +383,9 @@ ipv6rs_handledata(_unused void *arg)
        struct nd_opt_hdr *ndo;
        struct ra_opt *rao;
        struct ipv6_addr *ap;
-       char *opt;
+       char *opt, *tmp;
        struct timeval expire;
-       int has_dns, new_rap;
+       int has_dns, new_rap, new_data;
 
        len = recvmsg(sock, &rcvhdr, 0);
        if (len == -1) {
@@ -438,7 +442,9 @@ ipv6rs_handledata(_unused void *arg)
                if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
                        break;
        if (ifp == NULL) {
-               syslog(LOG_ERR, "RA for unexpected interface from %s", sfrom);
+#ifdef DEBUG_RS
+               syslog(LOG_DEBUG, "RA for unexpected interface from %s", sfrom);
+#endif
                return;
        }
        TAILQ_FOREACH(rap, &ipv6_routers, next) {
@@ -460,9 +466,11 @@ ipv6rs_handledata(_unused void *arg)
                        rap->ns = NULL;
                        rap->nslen = 0;
                }
+               new_data = 1;
                syslog(LOG_INFO, "%s: Router Advertisement from %s",
                    ifp->name, sfrom);
-       }
+       } else
+               new_data = 0;
 
        if (rap == NULL) {
                rap = xzalloc(sizeof(*rap));
@@ -642,7 +650,10 @@ ipv6rs_handledata(_unused void *arg)
                                    ifp->name);
                        } else {
                                opt = xmalloc(l);
-                               decode_rfc3397(opt, l, n, op);
+                               tmp = xmalloc(l);
+                               decode_rfc3397(tmp, l, n, op);
+                               l = print_string(opt, l, l - 1, (uint8_t *)tmp);
+                               free(tmp);
                        }
                        break;
                }
@@ -691,7 +702,7 @@ ipv6rs_handledata(_unused void *arg)
                        else if (ipv6_remove_subnet(rap, ap) == -1)
                                syslog(LOG_ERR, "ipv6_remove_subnet %m");
                        else
-                               syslog(ap->new ? LOG_INFO : LOG_DEBUG,
+                               syslog(LOG_DEBUG,
                                    "%s: vltime %d seconds, pltime %d seconds",
                                    ifp->name, ap->prefix_vltime,
                                    ap->prefix_pltime);
@@ -701,7 +712,7 @@ ipv6rs_handledata(_unused void *arg)
                ipv6_build_routes();
        run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT");
        if (options & DHCPCD_TEST)
-               exit(EXIT_SUCCESS);
+               goto handle_flag;
 
        /* If we don't require RDNSS then set has_dns = 1 so we fork */
        if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
@@ -714,7 +725,8 @@ ipv6rs_handledata(_unused void *arg)
        ipv6rs_expire(ifp);
        if (has_dns)
                daemonise();
-       else if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED))
+       else if (options & DHCPCD_DAEMONISE &&
+           !(options & DHCPCD_DAEMONISED) && new_data)
                syslog(LOG_WARNING,
                    "%s: did not fork due to an absent RDNSS option in the RA",
                    ifp->name);
@@ -727,6 +739,22 @@ ipv6rs_handledata(_unused void *arg)
                rap->nsprobes = 0;
                ipv6ns_sendprobe(rap);
        }
+
+handle_flag:
+       if (rap->flags & ND_RA_FLAG_MANAGED) {
+               if (new_data)
+                       syslog(LOG_WARNING, "%s: no support for DHCPv6 management",
+                           ifp->name);
+//             if (dhcp6_start(ifp, 1) == -1)
+//                     syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
+       } else if (rap->flags & ND_RA_FLAG_OTHER) {
+               if (dhcp6_start(ifp, 0) == -1)
+                       syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
+       } else {
+               if (new_data)
+                       syslog(LOG_DEBUG, "%s: No DHCPv6 instruction in RA",
+                           ifp->name);
+       }
 }
 
 int
diff --git a/net.c b/net.c
index ae373c1687ee8772907bf65504ee36cb8ee5f651..3ae3f0101ad402206b5a387af5675d9861554bc1 100644 (file)
--- a/net.c
+++ b/net.c
@@ -65,6 +65,7 @@
 #include "config.h"
 #include "common.h"
 #include "dhcp.h"
+#include "dhcp6.h"
 #include "if-options.h"
 #include "ipv6rs.h"
 #include "net.h"
@@ -187,6 +188,7 @@ free_interface(struct interface *iface)
 {
        if (!iface)
                return;
+       dhcp6_free(iface);
        ipv6rs_free(iface);
        if (iface->state) {
                free_options(iface->state->options);