]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Handle ND options in the same way we handle DHCP and DHCPv6 options.
authorRoy Marples <roy@marples.name>
Thu, 14 May 2015 19:46:21 +0000 (19:46 +0000)
committerRoy Marples <roy@marples.name>
Thu, 14 May 2015 19:46:21 +0000 (19:46 +0000)
16 files changed:
Makefile
common.c
common.h
dhcp.c
dhcpcd-definitions.conf
dhcpcd-embedded.h.in
dhcpcd-hooks/20-resolv.conf
dhcpcd.c
dhcpcd.conf.5.in
dhcpcd.h
genembedh
if-options.c
if-options.h
ipv6.c
ipv6nd.c
ipv6nd.h

index 36ff297aa66ed980931d0019272ce75d5b69e972..8b17c39bf911fa3a9e95e1e953e1ea71cf833f35 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -85,7 +85,7 @@ CLEANFILES+=  dhcpcd-embedded.h dhcpcd-embedded.c
 dhcpcd-embedded.h: genembedh dhcpcd-definitions.conf dhcpcd-embedded.h.in
        ${HOST_SH} ${.ALLSRC} $^ > $@
 
-dhcpcd-embedded.c: genembedc dhcpcd-definitions.conf
+dhcpcd-embedded.c: genembedc dhcpcd-definitions.conf dhcpcd-embedded.h
        ${HOST_SH} ${.ALLSRC} $^ > $@
 
 if-options.c: dhcpcd-embedded.h
index 71668cb9a17705bf45f0d84799aa68ea3b39114a..5317dcc80d55403661fc34037152de9fdc2fb68b 100644 (file)
--- a/common.c
+++ b/common.c
@@ -203,33 +203,44 @@ logger(struct dhcpcd_ctx *ctx, int pri, const char *fmt, ...)
 
 ssize_t
 setvar(struct dhcpcd_ctx *ctx,
-    char ***e, const char *prefix, const char *var, const char *value)
+    char **e, const char *prefix, const char *var, const char *value)
 {
        size_t len = strlen(var) + strlen(value) + 3;
 
        if (prefix)
                len += strlen(prefix) + 1;
-       **e = malloc(len);
-       if (**e == NULL) {
+       *e = malloc(len);
+       if (*e == NULL) {
                logger(ctx, LOG_ERR, "%s: %m", __func__);
                return -1;
        }
        if (prefix)
-               snprintf(**e, len, "%s_%s=%s", prefix, var, value);
+               snprintf(*e, len, "%s_%s=%s", prefix, var, value);
        else
-               snprintf(**e, len, "%s=%s", var, value);
-       (*e)++;
+               snprintf(*e, len, "%s=%s", var, value);
+       return (ssize_t)len;
+}
+
+ssize_t
+addvar(struct dhcpcd_ctx *ctx,
+    char ***e, const char *prefix, const char *var, const char *value)
+{
+       ssize_t len;
+
+       len = setvar(ctx, *e, prefix, var, value);
+       if (len != -1)
+               (*e)++;
        return (ssize_t)len;
 }
 
 ssize_t
-setvard(struct dhcpcd_ctx *ctx,
+addvard(struct dhcpcd_ctx *ctx,
     char ***e, const char *prefix, const char *var, size_t value)
 {
        char buffer[32];
 
        snprintf(buffer, sizeof(buffer), "%zu", value);
-       return setvar(ctx, e, prefix, var, buffer);
+       return addvar(ctx, e, prefix, var, buffer);
 }
 
 
index 9a0c0c159e8ddc3568c74a454b2e0debbb9c3802..619277a4f025fad8347579e05c9d40b3dbe77776 100644 (file)
--- a/common.h
+++ b/common.h
@@ -184,8 +184,10 @@ void logger_close(struct dhcpcd_ctx *);
 #endif
 
 ssize_t setvar(struct dhcpcd_ctx *,
+    char **, const char *, const char *, const char *);
+ssize_t addvar(struct dhcpcd_ctx *,
     char ***, const char *, const char *, const char *);
-ssize_t setvard(struct dhcpcd_ctx *,
+ssize_t addvard(struct dhcpcd_ctx *,
     char ***, const char *, const char *, size_t);
 time_t uptime(void);
 
diff --git a/dhcp.c b/dhcp.c
index 4588d090af5c507c1739bef8a12da145c69d99b6..3fb9ddd3a38af96d8c3970f8e6c97b4de91c04f3 100644 (file)
--- a/dhcp.c
+++ b/dhcp.c
@@ -1274,35 +1274,35 @@ dhcp_env(char **env, const char *prefix, const struct dhcp_message *dhcp,
                /* Set some useful variables that we derive from the DHCP
                 * message but are not necessarily in the options */
                addr.s_addr = dhcp->yiaddr ? dhcp->yiaddr : dhcp->ciaddr;
-               setvar(ifp->ctx, &ep, prefix, "ip_address", inet_ntoa(addr));
+               addvar(ifp->ctx, &ep, prefix, "ip_address", inet_ntoa(addr));
                if (get_option_addr(ifp->ctx, &net,
                    dhcp, DHO_SUBNETMASK) == -1) {
                        net.s_addr = ipv4_getnetmask(addr.s_addr);
-                       setvar(ifp->ctx, &ep, prefix,
+                       addvar(ifp->ctx, &ep, prefix,
                            "subnet_mask", inet_ntoa(net));
                }
                snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
-               setvar(ifp->ctx, &ep, prefix, "subnet_cidr", cidr);
+               addvar(ifp->ctx, &ep, prefix, "subnet_cidr", cidr);
                if (get_option_addr(ifp->ctx, &brd,
                    dhcp, DHO_BROADCAST) == -1) {
                        brd.s_addr = addr.s_addr | ~net.s_addr;
-                       setvar(ifp->ctx, &ep, prefix,
+                       addvar(ifp->ctx, &ep, prefix,
                            "broadcast_address", inet_ntoa(brd));
                }
                addr.s_addr = dhcp->yiaddr & net.s_addr;
-               setvar(ifp->ctx, &ep, prefix,
+               addvar(ifp->ctx, &ep, prefix,
                    "network_number", inet_ntoa(addr));
        }
 
        if (*dhcp->bootfile && !(overl & 1)) {
                print_string(safe, sizeof(safe), STRING,
                    dhcp->bootfile, sizeof(dhcp->bootfile));
-               setvar(ifp->ctx, &ep, prefix, "filename", safe);
+               addvar(ifp->ctx, &ep, prefix, "filename", safe);
        }
        if (*dhcp->servername && !(overl & 2)) {
                print_string(safe, sizeof(safe), STRING | DOMAIN,
                    dhcp->servername, sizeof(dhcp->servername));
-               setvar(ifp->ctx, &ep, prefix, "server_name", safe);
+               addvar(ifp->ctx, &ep, prefix, "server_name", safe);
        }
 
        /* Zero our indexes */
index 58eba076cf8396c1f27572c41a06b712714bafa6..d2991c51f54fce7f9758a645c7dc7226ee4044ba 100644 (file)
@@ -286,6 +286,37 @@ encap 255  flag                    global
 # Options 224-254 are reserved for Private Use
 # Option 255 End
 
+##############################################################################
+# ND6 options, RFC4861
+definend 1     binhex                  source_address
+definend 2     binhex                  target_address
+
+# FIXME: L and A flag need to be extracted from reserved1
+definend 3     index embed             prefix_information
+embed          byte                    length
+embed          byte                    reserved1
+embed          uint32                  vltime
+embed          uint32                  pltime
+embed          uint32                  reserved2
+embed          array ip6address        prefix
+
+# option 4 is only for Redirect messages
+
+definend 5     embed                   mtu
+embed          uint16                  reserved
+embed          uint32                  mtu
+
+# ND6 options, RFC6101
+definend 25    index embed             rdnss
+embed          uint16                  reserved
+embed          uint32                  lifetime
+embed          array ip6address        servers
+
+definend 31    index embed             dnssl
+embed          uint16                  reserved
+embed          uint32                  lifetime
+embed          domain                  search
+
 ##############################################################################
 # DHCPv6 options, RFC3315
 define6 1      binhex                  client_id
index 3d153163587d2bb249628007ccf6eb76f6d43890..5e5096e59e24f8edc7622d23d3734e85e446cb13 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #define INITDEFINES    @INITDEFINES@
+#define INITDEFINENDS  @INITDEFINENDS@
 #define INITDEFINE6S   @INITDEFINE6S@
 
 extern const char * const dhcpcd_embedded_conf[];
index c9f7bedb70a0d463e7d92fad7edcba39695a0ccb..e010ab7cf4e279dd3437cd9bae474f03b13d7bcf 100644 (file)
@@ -70,15 +70,32 @@ build_resolv_conf()
 
 add_resolv_conf()
 {
-       local x= conf="$signature$NL" i=${ra_count:-0} ra= warn=true
-
-       while [ $i -ne 0 ]; do
-               eval ra=\$ra${i}_rdnss
-               new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$ra"
-               eval ra=\$ra${i}_dnssl
-               new_domain_search="$new_domain_search${new_domain_search:+ }$ra"
-               i=$(($i - 1))
+       local x= conf="$signature$NL" warn=true
+       local i j rdnss dnssl new_rdnss new_dnssl
+
+       # Extract any ND DNS options from the RA
+       i=1
+       j=1
+       while true; do
+               while true; do
+                       eval rdnss=\$nd${i}_rdnss${j}_servers
+                       eval dnssl=\$nd${i}_dnssl${j}_search
+                       [ -z "$rdnss" -a -z "$dnssl" ] && break
+                       new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
+                       new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
+                       j=$(($j + 1))
+               done
+               i=$(($i + 1))
+               j=1
+               eval rdnss=\$nd${i}_rdnss${j}_servers
+               eval dnssl=\$nd${i}_dnssl${j}_search
+               [ -z "$rdnss" -a -z "$dnssl" ] && break
+               new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
+               new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
+               j=$(($j + 1))
        done
+       new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
+       new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl"
 
        # If we don't have any configuration, remove it
        if [ -z "$new_domain_name_servers" -a \
index 27c5ac9191e5292af609e9deaefd0119987a278e..8403626440d39a692f765065326603183712d470 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -164,6 +164,14 @@ free_globals(struct dhcpcd_ctx *ctx)
        }
 #endif
 #ifdef INET6
+       if (ctx->nd_opts) {
+               for (opt = ctx->nd_opts;
+                   ctx->nd_opts_len > 0;
+                   opt++, ctx->nd_opts_len--)
+                       free_dhcp_opt_embenc(opt);
+               free(ctx->nd_opts);
+               ctx->nd_opts = NULL;
+       }
        if (ctx->dhcp6_opts) {
                for (opt = ctx->dhcp6_opts;
                    ctx->dhcp6_opts_len > 0;
@@ -1454,6 +1462,9 @@ main(int argc, char **argv)
 #endif
 #ifdef INET6
                if (family == 0 || family == AF_INET6) {
+                       printf("\nND options:\n");
+                       ipv6nd_printoptions(&ctx,
+                           ifo->nd_override, ifo->nd_override_len);
                        printf("\nDHCPv6 options:\n");
                        dhcp6_printoptions(&ctx,
                            ifo->dhcp6_override, ifo->dhcp6_override_len);
index 905a924fbc5af641e49f204cbebda7205264a33d..ab072da79b280d2e0458272aac1eca7068eaf615 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd April 6, 2015
+.Dd May 14, 2015
 .Dt DHCPCD.CONF 5
 .Os
 .Sh NAME
@@ -462,14 +462,39 @@ You can specify more
 separated by commas, spaces or more
 .Ic option
 lines.
+.Ar option
 Prepend dhcp6_ to
 .Ar option
 to request a DHCPv6 option.
 DHCPv4 options are mapped to DHCPv6 where applicable.
+.Pp
+Prepend nd_ to
+.Ar option
+to handle ND options, but this only works for the
+.Ic nooption ,
+.Ic reject
+and
+.Ic require
+options.
 .It Ic nooption Ar option
-Remove the option from the DHCP message.
-This should only be used when a DHCP server sends a non requested option
-that should not be processed.
+Remove the option from the message before it's processed.
+.It Ic require Ar option
+Requires the
+.Ar option
+to be present in all messages, otherwise the message is ignored.
+To enforce that
+.Nm dhcpcd
+only responds to DHCP servers and not BOOTP servers, you can
+.Ic require
+.Ar dhcp_message_type .
+This isn't an exact science though because a BOOTP server can send DHCP like
+options.
+.It Ic reject Ar option
+Reject a message that contains the
+.Ar option .
+This is useful when you cannot use
+.Ic require
+to select / de-select BOOTP messages.
 .It Ic destination Ar option
 If
 .Nm
@@ -499,27 +524,6 @@ will timeout before moving back to the DISCOVER phase.
 .It Ic release
 .Nm dhcpcd
 will release the lease prior to stopping the interface.
-.It Ic require Ar option
-Requires the
-.Ar option
-to be present in all DHCP messages, otherwise the message is ignored.
-It can be a variable to be used in
-.Xr dhcpcd-run-hooks 8
-or the numerical value.
-You can specify more options separated by commas, spaces or more require lines.
-To enforce that
-.Nm dhcpcd
-only responds to DHCP servers and not BOOTP servers, you can
-.Ic require
-.Ar dhcp_message_type .
-This isn't an exact science though because a BOOTP server can send DHCP like
-options.
-.It Ic reject Ar option
-Reject a DHCP message that contains the
-.Ar option .
-This is useful when you cannot use
-.Ic require
-to select / de-select BOOTP messages.
 .It Ic script Ar script
 Use
 .Ar script
@@ -630,9 +634,10 @@ Use the last four bytes of the hardware address as the DHCP xid instead
 of a randomly generated number.
 .El
 .Ss Defining new options
-DHCP allows for the use of custom options.
+DHCP, ND and DHCPv6 allow for the use of custom options.
 Each option needs to be started with the
-.Ic define
+.Ic define ,
+.If definend
 or
 .Ic define6
 directive.
@@ -655,6 +660,17 @@ with a name of
 .Ar variable
 exported to
 .Xr dhcpcd-run-hooks 8 .
+.It Ic definend Ar code Ar type Ar variable
+Defines the ND option
+.Ar code
+of
+.Ar type
+with a name of
+.Ar variable
+exported to
+.Xr dhcpcd-run-hooks 8 ,
+with a prefix of
+.Va _nd .
 .It Ic define6 Ar code Ar type Ar variable
 Defines the DHCPv6 option
 .Ar code
index d89bbffce08c98deb51726ed3d45b5ddce438779..6d7223edb32531a496c63ae10dc88725bba7e084 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -135,6 +135,8 @@ struct dhcpcd_ctx {
        unsigned char secret[SECRET_LEN];
        size_t secret_len;
 
+       struct dhcp_opt *nd_opts;
+       size_t nd_opts_len;
        struct dhcp_opt *dhcp6_opts;
        size_t dhcp6_opts_len;
        struct ipv6_ctx *ipv6;
index a155cfae435fa24174f6800eefc07875fef79da9..cd97cbb44d786a01e1553f52596a706f4f2117af 100755 (executable)
--- a/genembedh
+++ b/genembedh
@@ -8,8 +8,10 @@ CONF=${1:-dhcpcd-definitions.conf}
 H=${2:-dhcpcd-embedded.h.in}
 
 INITDEFINES=$($TOOL_GREP "^define " $CONF | $TOOL_WC -l)
+INITDEFINENDS=$($TOOL_GREP "^definend " $CONF | $TOOL_WC -l)
 INITDEFINE6S=$($TOOL_GREP "^define6 " $CONF | $TOOL_WC -l)
 $TOOL_SED \
        -e "s/@INITDEFINES@/$INITDEFINES/" \
+       -e "s/@INITDEFINENDS@/$INITDEFINENDS/" \
        -e "s/@INITDEFINE6S@/$INITDEFINE6S/" \
        $H
index 85b6e8e5e662c41aff84b01b77be36030a01297f..8bb1185b27fe2e7ec89fb06d55119e6c8ad340ca 100644 (file)
 #define O_REJECT               O_BASE + 40
 #define O_IPV6RA_ACCEPT_NOPUBLIC       O_BASE + 41
 #define O_BOOTP                        O_BASE + 42
+#define O_DEFINEND             O_BASE + 43
 
 const struct option cf_options[] = {
        {"background",      no_argument,       NULL, 'b'},
@@ -175,6 +176,7 @@ const struct option cf_options[] = {
        {"dev",             required_argument, NULL, O_DEV},
        {"nodev",           no_argument,       NULL, O_NODEV},
        {"define",          required_argument, NULL, O_DEFINE},
+       {"definend",        required_argument, NULL, O_DEFINEND},
        {"define6",         required_argument, NULL, O_DEFINE6},
        {"embed",           required_argument, NULL, O_EMBED},
        {"encap",           required_argument, NULL, O_ENCAP},
@@ -519,11 +521,22 @@ set_option_space(struct dhcpcd_ctx *ctx,
 {
 
 #if !defined(INET) && !defined(INET6)
-       /* Satisfy use */
-       ctx = NULL;
+       UNUSED(ctx);
 #endif
 
 #ifdef INET6
+       if (strncmp(arg, "nd_", strlen("nd_")) == 0) {
+               *d = ctx->nd_opts;
+               *dl = ctx->nd_opts_len;
+               *od = ifo->nd_override;
+               *odl = ifo->nd_override_len;
+               *request = ifo->requestmasknd;
+               *require = ifo->requiremasknd;
+               *no = ifo->nomasknd;
+               *reject = ifo->rejectmasknd;
+               return arg + strlen("nd_");
+       }
+
        if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
                *d = ctx->dhcp6_opts;
                *dl = ctx->dhcp6_opts_len;
@@ -1466,6 +1479,12 @@ err_sla:
                dop = &ifo->dhcp_override;
                dop_len = &ifo->dhcp_override_len;
                /* FALLTHROUGH */
+       case O_DEFINEND:
+               if (dop == NULL) {
+                       dop = &ifo->nd_override;
+                       dop_len = &ifo->nd_override_len;
+               }
+               /* FALLTHROUGH */
        case O_DEFINE6:
                if (dop == NULL) {
                        dop = &ifo->dhcp6_override;
@@ -1689,10 +1708,17 @@ err_sla:
                ndop->len = (size_t)l;
                ndop->var = np;
                /* Save the define for embed and encap options */
-               if (opt == O_DEFINE || opt == O_DEFINE6 || opt == O_VENDOPT)
+               switch (opt) {
+               case O_DEFINE:
+               case O_DEFINEND:
+               case O_DEFINE6:
+               case O_VENDOPT:
                        *ldop = ndop;
-               else if (opt == O_ENCAP)
+                       break;
+               case O_ENCAP:
                        *edop = ndop;
+                       break;
+               }
                break;
        case O_VENDCLASS:
                fp = strwhite(arg);
@@ -2100,6 +2126,14 @@ read_config(struct dhcpcd_ctx *ctx,
                        ifo->dhcp_override_len = INITDEFINES;
 #endif
 
+#if defined(INET6) && defined(INITDEFINENDS)
+               ifo->nd_override =
+                   calloc(INITDEFINENDS, sizeof(*ifo->nd_override));
+               if (ifo->nd_override == NULL)
+                       logger(ctx, LOG_ERR, "%s: %m", __func__);
+               else
+                       ifo->nd_override_len = INITDEFINENDS;
+#endif
 #if defined(INET6) && defined(INITDEFINE6S)
                ifo->dhcp6_override =
                    calloc(INITDEFINE6S, sizeof(*ifo->dhcp6_override));
@@ -2172,15 +2206,24 @@ read_config(struct dhcpcd_ctx *ctx,
                ifo->dhcp_override_len = 0;
 
 #ifdef INET6
+               ctx->nd_opts = ifo->nd_override;
+               ctx->nd_opts_len = ifo->nd_override_len;
                ctx->dhcp6_opts = ifo->dhcp6_override;
                ctx->dhcp6_opts_len = ifo->dhcp6_override_len;
 #else
+               for (i = 0, opt = ifo->nd_override;
+                   i < ifo->nd_override_len;
+                   i++, opt++)
+                       free_dhcp_opt_embenc(opt);
+               free(ifo->nd_override);
                for (i = 0, opt = ifo->dhcp6_override;
                    i < ifo->dhcp6_override_len;
                    i++, opt++)
                        free_dhcp_opt_embenc(opt);
                free(ifo->dhcp6_override);
 #endif
+               ifo->nd_override = NULL;
+               ifo->nd_override_len = 0;
                ifo->dhcp6_override = NULL;
                ifo->dhcp6_override_len = 0;
 
@@ -2334,6 +2377,11 @@ free_options(struct if_options *ifo)
                    opt++, ifo->dhcp_override_len--)
                        free_dhcp_opt_embenc(opt);
                free(ifo->dhcp_override);
+               for (opt = ifo->nd_override;
+                   ifo->nd_override_len > 0;
+                   opt++, ifo->nd_override_len--)
+                       free_dhcp_opt_embenc(opt);
+               free(ifo->nd_override);
                for (opt = ifo->dhcp6_override;
                    ifo->dhcp6_override_len > 0;
                    opt++, ifo->dhcp6_override_len--)
index 4813528f9987b57eb9e88709b5826bf538a045cc..a84de5f6adb219c2017e8070e44918e07686d95f 100644 (file)
@@ -153,11 +153,15 @@ struct if_options {
        uint8_t requiremask[256 / NBBY];
        uint8_t nomask[256 / NBBY];
        uint8_t rejectmask[256 / NBBY];
+       uint8_t dstmask[256 / NBBY];
+       uint8_t requestmasknd[(UINT16_MAX + 1) / NBBY];
+       uint8_t requiremasknd[(UINT16_MAX + 1) / NBBY];
+       uint8_t nomasknd[(UINT16_MAX + 1) / NBBY];
+       uint8_t rejectmasknd[(UINT16_MAX + 1) / NBBY];
        uint8_t requestmask6[(UINT16_MAX + 1) / NBBY];
        uint8_t requiremask6[(UINT16_MAX + 1) / NBBY];
        uint8_t nomask6[(UINT16_MAX + 1) / NBBY];
        uint8_t rejectmask6[(UINT16_MAX + 1) / NBBY];
-       uint8_t dstmask[256 / NBBY];
        uint32_t leasetime;
        time_t timeout;
        time_t reboot;
@@ -191,6 +195,8 @@ struct if_options {
 
        struct dhcp_opt *dhcp_override;
        size_t dhcp_override_len;
+       struct dhcp_opt *nd_override;
+       size_t nd_override_len;
        struct dhcp_opt *dhcp6_override;
        size_t dhcp6_override_len;
        uint32_t vivco_en;
diff --git a/ipv6.c b/ipv6.c
index b3a3a5f2b0bf37213bf8099ed9e41f4202643b81..1e6778f1df4520355eca06f211aec93e3c44f168 100644 (file)
--- a/ipv6.c
+++ b/ipv6.c
@@ -1580,9 +1580,7 @@ again:
        cbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));
        if (cbp)
                snprintf(ia->saddr, sizeof(ia->saddr), "%s/%d",
-                   cbp, ia->prefix_len);
-       else
-               ia->saddr[0] = '\0';
+                   cbp, ia->prefix_len); else ia->saddr[0] = '\0';
 
        TAILQ_INSERT_TAIL(&state->addrs, ia, next);
        return ia;
index e6ebd7bd4597a8f0236c8cbcc61c1980dfa1f46f..2e2af130a7380a94d8accbfbbe98c06dc5804082 100644 (file)
--- a/ipv6nd.c
+++ b/ipv6nd.c
@@ -154,6 +154,31 @@ static void ipv6nd_handledata(void *);
 #define IPV6_RECVPKTINFO IPV6_PKTINFO
 #endif
 
+void
+ipv6nd_printoptions(const struct dhcpcd_ctx *ctx,
+    const struct dhcp_opt *opts, size_t opts_len)
+{
+       size_t i, j;
+       const struct dhcp_opt *opt, *opt2;
+       int cols;
+
+       for (i = 0, opt = ctx->nd_opts;
+           i < ctx->nd_opts_len; i++, opt++)
+       {
+               for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
+                       if (opt2->option == opt->option)
+                               break;
+               if (j == opts_len) {
+                       cols = printf("%03d %s", opt->option, opt->var);
+                       dhcp_print_option_encoding(opt, cols);
+               }
+       }
+       for (i = 0, opt = opts; i < opts_len; i++, opt++) {
+               cols = printf("%03d %s", opt->option, opt->var);
+               dhcp_print_option_encoding(opt, cols);
+       }
+}
+
 static int
 ipv6nd_open(struct dhcpcd_ctx *dctx)
 {
@@ -332,7 +357,6 @@ ipv6nd_expire(struct interface *ifp, uint32_t seconds)
                        rap->received = now;
                        rap->expired = seconds ? 0 : 1;
                        if (seconds) {
-                               struct ra_opt *rao;
                                struct ipv6_addr *ap;
 
                                rap->lifetime = seconds;
@@ -343,9 +367,6 @@ ipv6nd_expire(struct interface *ifp, uint32_t seconds)
                                        }
                                }
                                ipv6_addaddrs(&rap->addrs);
-                               TAILQ_FOREACH(rao, &rap->options, next) {
-                                       timespecclear(&rao->expire);
-                               }
                        }
                }
        }
@@ -397,18 +418,6 @@ ipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, int flags)
        }
 }
 
-static void
-ipv6nd_free_opts(struct ra *rap)
-{
-       struct ra_opt *rao;
-
-       while ((rao = TAILQ_FIRST(&rap->options))) {
-               TAILQ_REMOVE(&rap->options, rao, next);
-               free(rao->option);
-               free(rao);
-       }
-}
-
 struct ipv6_addr *
 ipv6nd_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
     short flags)
@@ -435,19 +444,26 @@ ipv6nd_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
        return NULL;
 }
 
-void ipv6nd_freedrop_ra(struct ra *rap, int drop)
+static void
+ipv6nd_removefreedrop_ra(struct ra *rap, int remove, int drop)
 {
 
        eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap->iface);
        eloop_timeout_delete(rap->iface->ctx->eloop, NULL, rap);
-       if (!drop)
+       if (remove && !drop)
                TAILQ_REMOVE(rap->iface->ctx->ipv6->ra_routers, rap, next);
        ipv6_freedrop_addrs(&rap->addrs, drop, NULL);
-       ipv6nd_free_opts(rap);
        free(rap->data);
        free(rap);
 }
 
+void
+ipv6nd_freedrop_ra(struct ra *rap, int drop)
+{
+
+       ipv6nd_removefreedrop_ra(rap, 1, drop);
+}
+
 ssize_t
 ipv6nd_free(struct interface *ifp)
 {
@@ -531,7 +547,6 @@ ipv6nd_scriptrun(struct ra *rap)
 {
        int hasdns, hasaddress, pid;
        struct ipv6_addr *ap;
-       const struct ra_opt *rao;
 
        hasaddress = 0;
        /* If all addresses have completed DAD run the script */
@@ -557,16 +572,7 @@ ipv6nd_scriptrun(struct ra *rap)
        if (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS))
                hasdns = 1;
        else {
-               hasdns = 0;
-               TAILQ_FOREACH(rao, &rap->options, next) {
-                       if (rao->type == ND_OPT_RDNSS &&
-                           rao->option &&
-                           timespecisset(&rao->expire))
-                       {
-                               hasdns = 1;
-                               break;
-                       }
-               }
+               hasdns = rap->hasdns;
        }
 
        script_runreason(rap->iface, "ROUTERADVERT");
@@ -729,24 +735,20 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
     struct icmp6_hdr *icp, size_t len)
 {
        struct ipv6_ctx *ctx = dctx->ipv6;
-       size_t olen, l, n;
-       ssize_t r;
+       size_t i, olen;
        struct nd_router_advert *nd_ra;
        struct nd_opt_prefix_info *pi;
        struct nd_opt_mtu *mtu;
        struct nd_opt_rdnss *rdnss;
-       struct nd_opt_dnssl *dnssl;
        uint32_t lifetime, mtuv;
-       uint8_t *p, *op;
-       struct in6_addr addr;
+       uint8_t *p;
        char buf[INET6_ADDRSTRLEN];
        const char *cbp;
        struct ra *rap;
        struct nd_opt_hdr *ndo;
-       struct ra_opt *rao;
        struct ipv6_addr *ap;
-       char *opt, *opt2, *tmp;
-       struct timespec expire;
+       char *opt, *opt2;
+       struct dhcp_opt *dho;
        uint8_t new_rap, new_data;
 #ifdef IPV6_MANAGETEMPADDR
        uint8_t new_ap;
@@ -830,7 +832,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
                rap->from = ctx->from.sin6_addr;
                strlcpy(rap->sfrom, ctx->sfrom, sizeof(rap->sfrom));
                TAILQ_INIT(&rap->addrs);
-               TAILQ_INIT(&rap->options);
                new_rap = 1;
        } else
                new_rap = 0;
@@ -861,6 +862,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
                rap->retrans = ntohl(nd_ra->nd_ra_retransmit);
        if (rap->lifetime)
                rap->expired = 0;
+       rap->hasdns = 0;
 
        ipv6_settempstale(ifp);
        TAILQ_FOREACH(ap, &rap->addrs, next) {
@@ -889,6 +891,34 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
                        break;
                }
 
+               if (has_option_mask(ifp->options->rejectmasknd,
+                   ndo->nd_opt_type))
+               {
+                       for (i = 0, dho = dctx->nd_opts;
+                           i < dctx->nd_opts_len;
+                           i++, dho++)
+                       {
+                               if (dho->option == ndo->nd_opt_type)
+                                       break;
+                       }
+                       if (dho != NULL)                
+                               logger(ifp->ctx, LOG_WARNING,
+                                   "%s: reject RA (option %s) from %s",
+                                   ifp->name, dho->var, ctx->sfrom);
+                       else
+                               logger(ifp->ctx, LOG_WARNING,
+                                   "%s: reject RA (option %d) from %s",
+                                   ifp->name, ndo->nd_opt_type, ctx->sfrom);
+                       if (new_rap)
+                               ipv6nd_removefreedrop_ra(rap, 0, 0);
+                       else
+                               ipv6nd_free_ra(rap);
+                       return;
+               }
+
+               if (has_option_mask(ifp->options->nomasknd, ndo->nd_opt_type))
+                       continue;
+
                opt = opt2 = NULL;
                switch (ndo->nd_opt_type) {
                case ND_OPT_PREFIX_INFORMATION:
@@ -996,16 +1026,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
                        ap->prefix_pltime =
                            ntohl(pi->nd_opt_pi_preferred_time);
                        ap->nsprobes = 0;
-                       cbp = inet_ntop(AF_INET6, &ap->prefix, buf, sizeof(buf));
-                       if (cbp) {
-                               l = strlen(cbp);
-                               opt = malloc(l + 5);
-                               if (opt) {
-                                       snprintf(opt, l + 5, "%s/%d", cbp,
-                                           ap->prefix_len);
-                                       opt2 = strdup(ap->saddr);
-                               }
-                       }
 
 #ifdef IPV6_MANAGETEMPADDR
                        /* RFC4941 Section 3.3.3 */
@@ -1038,140 +1058,40 @@ ipv6nd_handlera(struct dhcpcd_ctx *dctx, struct interface *ifp,
                                break;
                        }
                        rap->mtu = mtuv;
-                       snprintf(buf, sizeof(buf), "%d", mtuv);
-                       opt = strdup(buf);
                        break;
 
                case ND_OPT_RDNSS:
-                       rdnss = (struct nd_opt_rdnss *)p;
-                       lifetime = ntohl(rdnss->nd_opt_rdnss_lifetime);
-                       op = (uint8_t *)ndo;
-                       op += offsetof(struct nd_opt_rdnss,
-                           nd_opt_rdnss_lifetime);
-                       op += sizeof(rdnss->nd_opt_rdnss_lifetime);
-                       l = 0;
-                       for (n = (size_t)ndo->nd_opt_len - 1; n > 1; n -= 2,
-                           op += sizeof(addr))
-                       {
-                               r = ipv6_printaddr(NULL, 0, op, ifp->name);
-                               if (r != -1)
-                                       l += (size_t)r + 1;
-                       }
-                       op = (uint8_t *)ndo;
-                       op += offsetof(struct nd_opt_rdnss,
-                           nd_opt_rdnss_lifetime);
-                       op += sizeof(rdnss->nd_opt_rdnss_lifetime);
-                       tmp = opt = malloc(l);
-                       if (opt == NULL)
-                               continue;
-                       for (n = (size_t)ndo->nd_opt_len - 1; n > 1; n -= 2,
-                           op += sizeof(addr))
-                       {
-                               r = ipv6_printaddr(tmp, l, op,
-                                   ifp->name);
-                               if (r != -1) {
-                                       l -= ((size_t)r + 1);
-                                       tmp += (size_t)r;
-                                       *tmp++ = ' ';
-                               }
-                       }
-                       if (tmp != opt)
-                               (*--tmp) = '\0';
-                       else
-                               *opt = '\0';
-                       break;
-
-               case ND_OPT_DNSSL:
-                       dnssl = (struct nd_opt_dnssl *)p;
-                       lifetime = ntohl(dnssl->nd_opt_dnssl_lifetime);
-                       op = p + offsetof(struct nd_opt_dnssl,
-                           nd_opt_dnssl_lifetime);
-                       op += sizeof(dnssl->nd_opt_dnssl_lifetime);
-                       n = (size_t)(dnssl->nd_opt_dnssl_len - 1) * 8;
-                       r = decode_rfc3397(NULL, 0, op, n);
-                       if (r < 1) {
-                               logger(ifp->ctx, new_data ? LOG_ERR : LOG_DEBUG,
-                                   "%s: invalid DNSSL option",
-                                   ifp->name);
-                               continue;
-                       } else {
-                               l = (size_t)r + 1;
-                               tmp = malloc(l);
-                               if (tmp) {
-                                       decode_rfc3397(tmp, l, op, n);
-                                       l -= 1;
-                                       n = (size_t)print_string(NULL, 0,
-                                           STRING | ARRAY | DOMAIN,
-                                           (const uint8_t *)tmp, l);
-                                       n++;
-                                       opt = malloc(n);
-                                       if (opt) {
-                                               print_string(opt, n,
-                                                   STRING | ARRAY | DOMAIN,
-                                                   (const uint8_t *)tmp, l);
-                                       } else
-                                               logger(ifp->ctx, LOG_ERR,
-                                                   "%s: %m", __func__);
-                                       free(tmp);
-                               }
-                       }
-                       break;
+                       rdnss = (struct nd_opt_rdnss *)(void *)p;
+                       if (rdnss->nd_opt_rdnss_lifetime &&
+                           rdnss->nd_opt_rdnss_len > 1)
+                               rap->hasdns = 1;
 
                default:
                        continue;
                }
+       }
 
-               if (opt == NULL) {
-                       logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                       continue;
-               }
-
-               n = ndo->nd_opt_type;
-extra_opt:
-               TAILQ_FOREACH(rao, &rap->options, next) {
-                       if (rao->type == n &&
-                           strcmp(rao->option, opt) == 0)
-                               break;
-               }
-               if (lifetime == 0 || *opt == '\0') {
-                       if (rao) {
-                               TAILQ_REMOVE(&rap->options, rao, next);
-                               free(rao->option);
-                               free(rao);
-                       }
-                       free(opt);
-                       free(opt2);
-                       continue;
-               }
-
-               if (rao == NULL) {
-                       rao = malloc(sizeof(*rao));
-                       if (rao == NULL) {
-                               logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
-                               continue;
-                       }
-                       rao->type = (uint16_t)n;
-                       rao->option = opt;
-                       TAILQ_INSERT_TAIL(&rap->options, rao, next);
-               } else
-                       free(opt);
-               if (lifetime == ~0U)
-                       timespecclear(&rao->expire);
-               else {
-                       expire.tv_sec = (time_t)lifetime;
-                       expire.tv_nsec = 0;
-                       timespecadd(&rap->received, &expire, &rao->expire);
-               }
-               if (rao && rao->type == ND_OPT_PREFIX_INFORMATION && opt2) {
-                       n = _ND_OPT_PREFIX_ADDR;
-                       opt = opt2;
-                       opt2 = NULL;
-                       goto extra_opt;
+       for (i = 0, dho = dctx->nd_opts;
+           i < dctx->nd_opts_len;
+           i++, dho++)
+       {
+               if (has_option_mask(ifp->options->requiremasknd,
+                   dho->option))
+               {
+                       logger(ifp->ctx, LOG_WARNING,
+                           "%s: reject RA (no option %s) from %s",
+                           ifp->name, dho->var, ctx->sfrom);
+                       if (new_rap)
+                               ipv6nd_removefreedrop_ra(rap, 0, 0);
+                       else
+                               ipv6nd_free_ra(rap);
+                       return;
                }
        }
 
        if (new_rap)
                add_router(ifp->ctx->ipv6, rap);
+
        if (!ipv6nd_ra_has_public_addr(rap) &&
            !(rap->iface->options->options & DHCPCD_IPV6RA_ACCEPT_NOPUBLIC) &&
            (!(rap->flags & ND_RA_FLAG_MANAGED) ||
@@ -1289,119 +1209,144 @@ ipv6nd_hasradhcp(const struct interface *ifp)
        return 0;
 }
 
+static const uint8_t *
+ipv6nd_getoption(struct dhcpcd_ctx *ctx,
+    size_t *os, unsigned int *code, size_t *len,
+    const uint8_t *od, size_t ol, struct dhcp_opt **oopt)
+{
+       const struct nd_opt_hdr *o;
+       size_t i;
+       struct dhcp_opt *opt;
+
+       if (od) {
+               *os = sizeof(*o);
+               if (ol < *os) {
+                       errno = EINVAL;
+                       return NULL;
+               }
+               o = (const struct nd_opt_hdr *)od;
+               if (o->nd_opt_len > ol) {
+                       errno = EINVAL;
+                       return NULL;
+               }
+               *len = (o->nd_opt_len * 8) - sizeof(*o);
+               *code = o->nd_opt_type;
+       } else
+               o = NULL;
+
+       for (i = 0, opt = ctx->nd_opts;
+           i < ctx->nd_opts_len; i++, opt++)
+       {
+               if (opt->option == *code) {
+                       *oopt = opt;
+                       break;
+               }
+       }
+
+       if (o)
+               return ND_COPTION_DATA(o);
+       return NULL;
+}
+
 ssize_t
 ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
 {
-       size_t i, l, len;
-       const struct ra *rap;
-       const struct ra_opt *rao;
-       char buffer[32];
-       const char *optn;
-       char **pref, **addr, **mtu, **rdnss, **dnssl, ***var, *new;
+       size_t i, j, n, len;
+       struct ra *rap;
+       char ndprefix[32], abuf[24];
+       struct dhcp_opt *opt;
+       const struct nd_opt_hdr *o;
+       struct ipv6_addr *ia;
 
-       i = l = 0;
+       i = n = 0;
        TAILQ_FOREACH(rap, ifp->ctx->ipv6->ra_routers, next) {
                if (rap->iface != ifp)
                        continue;
                i++;
+               if (prefix != NULL)
+                       snprintf(ndprefix, sizeof(ndprefix),
+                           "%s_nd%zu", prefix, i);
+               else
+                       snprintf(ndprefix, sizeof(ndprefix),
+                           "nd%zu", i);
+               if (env)
+                       setvar(rap->iface->ctx, &env[n], ndprefix,
+                           "from", rap->sfrom);
+               n++;
+
+               /* Zero our indexes */
                if (env) {
-                       snprintf(buffer, sizeof(buffer),
-                           "ra%zu_from", i);
-                       setvar(ifp->ctx, &env, prefix, buffer, rap->sfrom);
+                       for (j = 0, opt = rap->iface->ctx->nd_opts;
+                           j < rap->iface->ctx->nd_opts_len;
+                           j++, opt++)
+                               dhcp_zero_index(opt);
+                       for (j = 0, opt = rap->iface->options->nd_override;
+                           j < rap->iface->options->nd_override_len;
+                           j++, opt++)
+                               dhcp_zero_index(opt);
                }
-               l++;
 
-               pref = addr = mtu = rdnss = dnssl = NULL;
-               TAILQ_FOREACH(rao, &rap->options, next) {
-                       if (rao->option == NULL)
-                               continue;
-                       var = NULL;
-                       switch(rao->type) {
-                       case ND_OPT_PREFIX_INFORMATION:
-                               optn = "prefix";
-                               var = &pref;
-                               break;
-                       case _ND_OPT_PREFIX_ADDR:
-                               optn = "addr";
-                               var = &addr;
-                               break;
-                       case ND_OPT_MTU:
-                               optn = "mtu";
-                               var = &mtu;
-                               break;
-                       case ND_OPT_RDNSS:
-                               optn = "rdnss";
-                               var = &rdnss;
-                               break;
-                       case ND_OPT_DNSSL:
-                               optn = "dnssl";
-                               var = &dnssl;
+               /* Unlike DHCP, ND6 options *may* occur more than once.
+                * There is also no provision for option concatenation
+                * unlike DHCP. */
+               len = rap->data_len;
+               if (ND_CFIRST_OPTION(rap))
+                       len -= (size_t)((const uint8_t *)ND_CFIRST_OPTION(rap)
+                           - rap->data);
+
+               for (o = ND_CFIRST_OPTION(rap);
+                   len >= (ssize_t)sizeof(*o);
+                   o = ND_CNEXT_OPTION(o))
+               {
+                       if (o->nd_opt_len * 8 > len) {
+                               errno = EINVAL;
                                break;
-                       default:
-                               continue;
                        }
-                       if (*var == NULL) {
-                               *var = env ? env : &new;
-                               l++;
-                       } else if (env) {
-                               /* With single only options, last one takes
-                                * precedence */
-                               if (rao->type == ND_OPT_MTU) {
-                                       new = strchr(**var, '=');
-                                       if (new == NULL) {
-                                               logger(ifp->ctx, LOG_ERR,
-                                                   "new is null");
-                                               continue;
-                                       } else
-                                               new++;
-                                       len = (size_t)(new - **var) +
-                                           strlen(rao->option) + 1;
-                                       if (len > strlen(**var))
-                                               new = realloc(**var, len);
-                                       else
-                                               new = **var;
-                                       if (new) {
-                                               **var = new;
-                                               new = strchr(**var, '=');
-                                               if (new) {
-                                                       len -=
-                                                           (size_t)
-                                                           (new - **var);
-                                                       strlcpy(new + 1,
-                                                           rao->option,
-                                                           len - 1);
-                                               } else
-                                                       logger(ifp->ctx,
-                                                           LOG_ERR,
-                                                           "new is null");
-                                       }
-                                       continue;
-                               }
-                               len = strlen(rao->option) + 1;
-                               new = realloc(**var, strlen(**var) + 1 + len);
-                               if (new) {
-                                       **var = new;
-                                       new += strlen(new);
-                                       *new++ = ' ';
-                                       strlcpy(new, rao->option, len);
-                               } else
-                                       logger(ifp->ctx, LOG_ERR,
-                                           "%s: %m", __func__);
+                       len -= o->nd_opt_len * 8;
+                       if (has_option_mask(rap->iface->options->nomasknd,
+                           o->nd_opt_type))
                                continue;
+                       for (j = 0, opt = rap->iface->options->nd_override;
+                           j < rap->iface->options->nd_override_len;
+                           j++, opt++)
+                               if (opt->option == o->nd_opt_type)
+                                       break;
+                       if (j == rap->iface->options->nd_override_len) {
+                               for (j = 0, opt = rap->iface->ctx->nd_opts;
+                                   j < rap->iface->ctx->nd_opts_len;
+                                   j++, opt++)
+                                       if (opt->option == o->nd_opt_type)
+                                               break;
+                               if (j == rap->iface->ctx->nd_opts_len)
+                                       opt = NULL;
+                       }
+                       if (opt) {
+                               n += dhcp_envoption(rap->iface->ctx,
+                                   env == NULL ? NULL : &env[n],
+                                   ndprefix, rap->iface->name,
+                                   opt, ipv6nd_getoption,
+                                   ND_COPTION_DATA(o), ND_OPTION_LEN(o));
                        }
+               }
+
+               /* We need to output the addresses we actually made
+                * from the prefix information options as well. */
+               j = 0;
+               TAILQ_FOREACH(ia, &rap->addrs, next) {
+                       if (!(ia->flags & IPV6_AF_AUTOCONF) ||
+                           ia->flags & IPV6_AF_TEMPORARY)
+                               continue;
+                       j++;
                        if (env) {
-                               snprintf(buffer, sizeof(buffer),
-                                   "ra%zu_%s", i, optn);
-                               setvar(ifp->ctx, &env,
-                                   prefix, buffer, rao->option);
+                               snprintf(abuf, sizeof(abuf), "addr%zu", j);
+                               setvar(rap->iface->ctx, &env[n], ndprefix,
+                                   abuf, ia->saddr);
                        }
+                       n++;
                }
+                       
        }
-
-       if (env)
-               setvard(ifp->ctx, &env, prefix, "ra_count", i);
-       l++;
-       return (ssize_t)l;
+       return (ssize_t)n;
 }
 
 void
@@ -1424,7 +1369,6 @@ ipv6nd_expirera(void *arg)
 {
        struct interface *ifp;
        struct ra *rap, *ran;
-       struct ra_opt *rao, *raon;
        struct timespec now, lt, expire, next;
        uint8_t expired, valid, validone;
 
@@ -1460,43 +1404,10 @@ ipv6nd_expirera(void *arg)
                        }
                }
 
-               /* Addresses are expired in ipv6_addaddrs
-                * so that DHCPv6 addresses can be removed also. */
-               TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) {
-                       if (rap->expired) {
-                               switch(rao->type) {
-                               case ND_OPT_RDNSS: /* FALLTHROUGH */
-                               case ND_OPT_DNSSL:
-                                       /* RFC6018 end of section 5.2 states
-                                        * that if tha RA has a lifetime of 0
-                                        * then we should expire these
-                                        * options */
-                                       TAILQ_REMOVE(&rap->options, rao, next);
-                                       expired = 1;
-                                       free(rao->option);
-                                       free(rao);
-                                       continue;
-                               }
-                       }
-                       if (!timespecisset(&rao->expire))
-                               continue;
-                       if (timespeccmp(&now, &rao->expire, >)) {
-                               /* Expired prefixes are logged above */
-                               if (rao->type != ND_OPT_PREFIX_INFORMATION)
-                                       logger(ifp->ctx, LOG_WARNING,
-                                           "%s: %s: expired option %d",
-                                           ifp->name, rap->sfrom, rao->type);
-                               TAILQ_REMOVE(&rap->options, rao, next);
-                               expired = 1;
-                               free(rao->option);
-                               free(rao);
-                               continue;
-                       }
-                       valid = 1;
-                       timespecsub(&rao->expire, &now, &lt);
-                       if (!timespecisset(&next) || timespeccmp(&next, &lt, >))
-                               next = lt;
-               }
+               /* XXX FixMe!
+                * We need to extract the lifetime from each option and check
+                * if that has expired or not.
+                * If it has, zero the option out in the returned data. */
 
                /* No valid lifetimes are left on the RA, so we might
                 * as well punt it. */
index 2ab76df33cb6911b733966032cf229572f659e57..cc4b67d712b6a3f7b4024868ab071dbd811aa282 100644 (file)
--- a/ipv6nd.h
+++ b/ipv6nd.h
 #include "dhcpcd.h"
 #include "ipv6.h"
 
-struct ra_opt {
-       TAILQ_ENTRY(ra_opt) next;
-       uint16_t type;
-       struct timespec expire;
-       char *option;
-};
-
 struct ra {
        TAILQ_ENTRY(ra) next;
        struct interface *iface;
@@ -55,7 +48,7 @@ struct ra {
        uint32_t retrans;
        uint32_t mtu;
        struct ipv6_addrhead addrs;
-       TAILQ_HEAD(, ra_opt) options;
+       uint8_t hasdns;
        uint8_t expired;
        uint8_t no_public_warned;
 };
@@ -71,6 +64,15 @@ struct rs_state {
 #define RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])
 #define RS_STATE_RUNNING(a) (ipv6nd_hasra((a)) && ipv6nd_dadcompleted((a)))
 
+#define ND_CFIRST_OPTION(m)                                                   \
+    ((const struct nd_opt_hdr *)                                              \
+        ((const uint8_t *)(m)->data + sizeof(struct nd_router_advert)))
+#define ND_OPTION_LEN(o) (((o)->nd_opt_len * 8) - sizeof(struct nd_opt_hdr))
+#define ND_CNEXT_OPTION(o)                                                    \
+    ((const struct nd_opt_hdr *)((const uint8_t *)(o) + ((o)->nd_opt_len * 8)))
+#define ND_COPTION_DATA(o)                                                    \
+    ((const uint8_t *)(o) + sizeof(struct nd_opt_hdr))
+
 #define MAX_RTR_SOLICITATION_DELAY     1       /* seconds */
 #define MAX_UNICAST_SOLICIT            3       /* 3 transmissions */
 #define RTR_SOLICITATION_INTERVAL      4       /* seconds */
@@ -91,6 +93,8 @@ struct rs_state {
 #define IPV6ND_ROUTER                  (1 << 1)
 
 #ifdef INET6
+void ipv6nd_printoptions(const struct dhcpcd_ctx *,
+    const struct dhcp_opt *, size_t);
 void ipv6nd_startrs(struct interface *);
 ssize_t ipv6nd_env(char **, const char *, const struct interface *);
 struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,