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
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);
}
#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);
/* 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 */
# 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
*/
#define INITDEFINES @INITDEFINES@
+#define INITDEFINENDS @INITDEFINENDS@
#define INITDEFINE6S @INITDEFINE6S@
extern const char * const dhcpcd_embedded_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 \
}
#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;
#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);
.\" 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
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
.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
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.
.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
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;
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
#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'},
{"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},
{
#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;
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;
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);
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));
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;
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--)
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;
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;
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;
#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)
{
rap->received = now;
rap->expired = seconds ? 0 : 1;
if (seconds) {
- struct ra_opt *rao;
struct ipv6_addr *ap;
rap->lifetime = seconds;
}
}
ipv6_addaddrs(&rap->addrs);
- TAILQ_FOREACH(rao, &rap->options, next) {
- timespecclear(&rao->expire);
- }
}
}
}
}
}
-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)
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)
{
{
int hasdns, hasaddress, pid;
struct ipv6_addr *ap;
- const struct ra_opt *rao;
hasaddress = 0;
/* If all addresses have completed DAD run the script */
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");
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;
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;
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) {
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:
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 */
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) ||
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
{
struct interface *ifp;
struct ra *rap, *ran;
- struct ra_opt *rao, *raon;
struct timespec now, lt, expire, next;
uint8_t expired, valid, validone;
}
}
- /* 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, <);
- if (!timespecisset(&next) || timespeccmp(&next, <, >))
- 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. */
#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;
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;
};
#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 */
#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 *,