When requesting a IA_PD and another IA type, create a psuedo interface

to handle the IA_PD.
ia_pd_mix config option disables this and mixes IA_PD in the single session as
per draft-ietf-dhc-dhcpv6-stateful-issues-06.
This commit is contained in:
Roy Marples 2014-07-07 14:41:18 +00:00
parent ab234549b1
commit 9d5cb9f924
8 changed files with 145 additions and 81 deletions

88
dhcp6.c
View File

@ -2221,6 +2221,8 @@ dhcp6_delegate_prefix(struct interface *ifp)
}
TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
if (ifp->options->options & DHCPCD_NOPFXDLG)
continue;
k = 0;
carrier_warned = abrt = 0;
TAILQ_FOREACH(ap, &state->addrs, next) {
@ -2357,6 +2359,24 @@ dhcp6_find_delegates(struct interface *ifp)
return k;
}
static struct interface *
dhcp6_findpfxdlgif(struct interface *ifp)
{
struct interface *ifn;
if (ifp->options && ifp->options->options & DHCPCD_PFXDLGONLY)
return NULL;
if (ifp->ctx && ifp->ctx->ifaces) {
TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
if (strcmp(ifn->name, ifp->name) == 0 &&
ifn->options->options & DHCPCD_PFXDLGONLY)
return ifn;
}
}
return NULL;
}
/* ARGSUSED */
static void
dhcp6_handledata(void *arg)
@ -2367,7 +2387,7 @@ dhcp6_handledata(void *arg)
ssize_t bytes;
struct cmsghdr *cm;
struct in6_pktinfo pkt;
struct interface *ifp;
struct interface *ifp, *ifpx;
const char *op;
struct dhcp6_message *r;
struct dhcp6_state *state;
@ -2431,22 +2451,31 @@ dhcp6_handledata(void *arg)
ctx->sfrom);
return;
}
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
/* Which interface state is the IAID for? */
ifpx = dhcp6_findpfxdlgif(ifp);
if (ifpx && D6_STATE(ifpx)) {
state = D6_STATE(ifpx);
if (r->xid[0] == state->send->xid[0] &&
r->xid[1] == state->send->xid[1] &&
r->xid[2] == state->send->xid[2])
ifp = ifpx;
}
state = D6_STATE(ifp);
if (state == NULL || state->send == NULL) {
syslog(LOG_DEBUG, "%s: DHCPv6 reply received but not running",
ifp->name);
return;
}
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
/* We're already bound and this message is for another machine */
/* XXX DELEGATED? */
if (r->type != DHCP6_RECONFIGURE &&
(state->state == DH6S_BOUND || state->state == DH6S_INFORMED))
return;
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
if (r->type != DHCP6_RECONFIGURE &&
(r->xid[0] != state->send->xid[0] ||
r->xid[1] != state->send->xid[1] ||
@ -2929,6 +2958,47 @@ dhcp6_start1(void *arg)
if (dhcp6_findselfsla(ifp, NULL))
del_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT);
/* Create a 2nd interface to handle the PD state */
if (!(ifo->options & (DHCPCD_PFXDLGONLY | DHCPCD_PFXDLGMIX)) &&
dhcp6_hasprefixdelegation(ifp))
{
const char * const argv[] = { ifp->name };
struct if_head *ifs;
struct interface *ifn;
ifn = dhcp6_findpfxdlgif(ifp);
if (ifn == NULL) {
ifs = if_discover(ifp->ctx, -1, UNCONST(argv));
if (ifs) {
ifn = TAILQ_FIRST(ifs);
if (ifn) {
syslog(LOG_INFO,
"%s: creating psuedo interface"
" to handle Prefix Delegation",
ifp->name);
ifp->options->options |=
DHCPCD_NOPFXDLG;
TAILQ_REMOVE(ifs, ifn, next);
TAILQ_INSERT_AFTER(ifp->ctx->ifaces,
ifp, ifn, next);
dhcpcd_initstate(ifn);
ifn->options->options |=
DHCPCD_PFXDLGONLY;
ifn->options->options &=
~(DHCPCD_IPV4 | DHCPCD_IPV6RS |
DHCPCD_NOPFXDLG);
eloop_timeout_add_sec(ifp->ctx->eloop,
0, dhcpcd_startinterface, ifn);
}
while ((ifn = TAILQ_FIRST(ifs))) {
TAILQ_REMOVE(ifs, ifn, next);
if_free(ifn);
}
free(ifs);
}
}
}
if (state->state == DH6S_INFORM) {
add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
dhcp6_startinform(ifp);
@ -3019,10 +3089,18 @@ dhcp6_reboot(struct interface *ifp)
static void
dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
{
struct interface *ifpx;
struct dhcp6_state *state;
struct dhcpcd_ctx *ctx;
unsigned long long options;
ifpx = dhcp6_findpfxdlgif(ifp);
if (ifpx) {
dhcp6_freedrop(ifpx, drop, reason);
TAILQ_REMOVE(ifp->ctx->ifaces, ifpx, next);
if_free(ifpx);
}
if (ifp->ctx->eloop)
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 4, 2014
.Dd July 7, 2014
.Dt DHCPCD 8
.Os
.Sh NAME
@ -68,10 +68,6 @@
.Fl U, Fl Fl dumplease
.Ar interface
.Nm
.Fl Fl pfxdlgonly
.Nm
.Fl Fl nopfxdlg
.Nm
.Fl Fl version
.Nm
.Fl x , Fl Fl exit
@ -556,6 +552,14 @@ Dumps the last lease for the
to stdout.
.Ar interface
could also be a path to a DHCP wire formatted file.
Use the
.Fl 4
or
.Fl 6
flags to specify an address family.
Pass a 2nd
.Fl U, Fl Fl dumplease option to dump a secondary lease, such as
DHCPv6 Prefix Delegation when not being mixed with another IA type.
.It Fl V, Fl Fl variables
Display a list of option codes and the associated variable for use in
.Xr dhcpcd-run-hooks 8 .
@ -618,30 +622,6 @@ to rebind, reconfigure or exit need to include the same restrictive flags
so that
.Nm
knows which process to signal.
.Pp
.Li RFC3633
DHCPv6 Prefix Delegation does not work in the same state or session as
Normal or Temporary Addresses.
.Nm
is designed for a single state per protocol and as such
.Li draft-ietf-dhc-dhcpv6-stateful-issues-06
is supported by default, but that is not a published RFC yet.
To allow RFC conformance,
.Nm
supports the
.Fl Fl pfxdlgonly
and
.Fl Fl nopfxdlg
options which allow the spawning of two
.Nm
instances to separate the Prefix Delegation state from the others.
You may wish to disable
.Xr dhcpcd-run-hooks 8
on the
.Fl Fl pfxdlgonly
instance using the
.Fl Fl script \"\"
option.
.Sh FILES
.Bl -ohang
.It Pa @SYSCONFDIR@/dhcpcd.conf

View File

@ -581,7 +581,7 @@ warn_iaid_conflict(struct interface *ifp, uint8_t *iaid)
}
/* This is only a problem if the interfaces are on the same network. */
if (ifn)
if (ifn && strcmp(ifp->name, ifn->name))
syslog(LOG_ERR,
"%s: IAID conflicts with one assigned to %s",
ifp->name, ifn->name);
@ -702,7 +702,7 @@ handle_link(void *arg)
}
static void
init_state(struct interface *ifp, int argc, char **argv)
dhcpcd_initstate1(struct interface *ifp, int argc, char **argv)
{
struct if_options *ifo;
@ -728,6 +728,13 @@ init_state(struct interface *ifp, int argc, char **argv)
}
}
void
dhcpcd_initstate(struct interface *ifp)
{
dhcpcd_initstate1(ifp, ifp->ctx->argc, ifp->ctx->argv);
}
static void
run_preinit(struct interface *ifp)
{
@ -794,7 +801,7 @@ dhcpcd_handleinterface(void *arg, int action, const char *ifname)
syslog(LOG_DEBUG, "%s: interface added", ifp->name);
TAILQ_REMOVE(ifs, ifp, next);
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
init_state(ifp, ctx->argc, ctx->argv);
dhcpcd_initstate(ifp);
run_preinit(ifp);
iff = ifp;
}
@ -874,7 +881,7 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
if_free(ifp);
} else {
TAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);
init_state(ifp, argc, argv);
dhcpcd_initstate1(ifp, argc, argv);
run_preinit(ifp);
dhcpcd_startinterface(ifp);
}
@ -887,7 +894,7 @@ reconf_reboot(struct dhcpcd_ctx *ctx, int action, int argc, char **argv, int oi)
static void
stop_all_interfaces(struct dhcpcd_ctx *ctx, int do_release)
{
struct interface *ifp;
struct interface *ifp, *ifpm;
/* drop_dhcp could change the order, so we do it like this. */
for (;;) {
@ -895,6 +902,10 @@ stop_all_interfaces(struct dhcpcd_ctx *ctx, int do_release)
ifp = TAILQ_LAST(ctx->ifaces, if_head);
if (ifp == NULL)
break;
/* Stop the master interface only */
ifpm = if_find(ifp->ctx, ifp->name);
if (ifpm)
ifp = ifpm;
if (do_release) {
ifp->options->options |= DHCPCD_RELEASE;
ifp->options->options &= ~DHCPCD_PERSISTENT;
@ -1267,10 +1278,13 @@ main(int argc, char **argv)
i = 1;
break;
case 'U':
i = 2;
if (i == 3)
i = 4;
else if (i != 4)
i = 3;
break;
case 'V':
i = 3;
i = 2;
break;
case '?':
usage();
@ -1292,7 +1306,7 @@ main(int argc, char **argv)
usage();
goto exit_failure;
}
if (i == 3) {
if (i == 2) {
printf("Interface options:\n");
if (optind == argc - 1) {
free_options(ifo);
@ -1324,6 +1338,8 @@ main(int argc, char **argv)
ctx.options |= DHCPCD_TEST;
else
ctx.options |= DHCPCD_DUMPLEASE;
if (i == 4)
ctx.options |= DHCPCD_PFXDLGONLY;
ctx.options |= DHCPCD_PERSISTENT;
ctx.options &= ~DHCPCD_DAEMONISE;
}
@ -1373,9 +1389,7 @@ main(int argc, char **argv)
snprintf(pidfile, sizeof(pidfile),
PIDFILE, "-", argv[optind], per);
} else {
snprintf(pidfile, sizeof(pidfile), PIDFILE,
ctx.options & DHCPCD_PFXDLGONLY ? ".pd" : "",
"", "");
snprintf(pidfile, sizeof(pidfile), PIDFILE, "", "", "");
ctx.options |= DHCPCD_MASTER;
}
}
@ -1414,6 +1428,8 @@ main(int argc, char **argv)
TAILQ_INSERT_HEAD(ctx.ifaces, ifp, next);
}
configure_interface(ifp, ctx.argc, ctx.argv);
if (ctx.options & DHCPCD_PFXDLGONLY)
ifp->options->options |= DHCPCD_PFXDLGONLY;
if (family == 0 || family == AF_INET) {
if (dhcp_dump(ifp) == -1)
i = 1;
@ -1428,7 +1444,7 @@ main(int argc, char **argv)
}
#ifdef USE_SIGNALS
if (!(ctx.options & (DHCPCD_TEST | DHCPCD_PFXDLGONLY)) &&
if (!(ctx.options & DHCPCD_TEST) &&
(sig == 0 || ctx.ifc != 0))
{
#endif
@ -1544,7 +1560,7 @@ main(int argc, char **argv)
}
if (ctx.options & DHCPCD_MASTER && !(ctx.options & DHCPCD_PFXDLGONLY)) {
if (ctx.options & DHCPCD_MASTER) {
if (control_start(&ctx, NULL) == -1)
syslog(LOG_ERR, "control_start: %m");
}
@ -1613,7 +1629,7 @@ main(int argc, char **argv)
}
TAILQ_FOREACH(ifp, ctx.ifaces, next) {
init_state(ifp, argc, argv);
dhcpcd_initstate1(ifp, argc, argv);
}
if (ctx.options & DHCPCD_BACKGROUND && dhcpcd_daemonise(&ctx))

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd July 4, 2014
.Dd July 7, 2014
.Dt DHCPCD.CONF 5
.Os
.Sh NAME
@ -293,12 +293,18 @@ IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
.D1 interface eth1
.D1 ipv4
.D1 ipv6rs
.Pp
Using this option with other IA options in the same interface block is not
currently RFC conformant.
Please see the
.Li BUGS
section below.
.It Ic ia_pd_mix
To be RFC compliant,
.Nm dhcpcd
cannot mix Prefix Delegation with other DHCPv6 address types in the same
session.
This has a number of issues: additional DHCP traffic and potential collisions
between options.
.Ic ia_pd_mix
enables
.Li draft-ietf-dhc-dhcpv6-stateful-issues-06
support so that Prefix Delegation can be mixed with other address types in
the same session.
.It Ic ipv4only
Only configure IPv4.
.It Ic ipv6only
@ -751,21 +757,5 @@ Same as
.Sh AUTHORS
.An Roy Marples Aq Mt roy@marples.name
.Sh BUGS
Combining different DHCPv6 IA options in the same interface block is not
currently RFC conformant.
See
.Lk http://datatracker.ietf.org/doc/draft-ietf-dhc-dhcpv6-stateful-issues
for details.
.Nm dhcpcd
strives to comply with this document and will be updated when finally published.
To enable RFC conformance, spawn separate
.Nm dhcpcd
instances using the
.Fl Fl pfxdlgonly
and
.Fl Fl nopfxdlg
options as described in
.Xr dhcpcd 8 .
.Pp
Please report them to
.Lk http://roy.marples.name/projects/dhcpcd

View File

@ -151,5 +151,6 @@ void dhcpcd_dropinterface(struct interface *, const char *);
int dhcpcd_selectprofile(struct interface *, const char *);
void dhcpcd_startinterface(void *);
void dhcpcd_initstate(struct interface *);
#endif

View File

@ -93,8 +93,7 @@
#define O_CONTROLGRP O_BASE + 34
#define O_SLAAC O_BASE + 35
#define O_GATEWAY O_BASE + 36
#define O_NOPFXDLG O_BASE + 37
#define O_PFXDLGONLY O_BASE + 38
#define O_PFXDLGMIX O_BASE + 37
const struct option cf_options[] = {
{"background", no_argument, NULL, 'b'},
@ -181,8 +180,7 @@ const struct option cf_options[] = {
{"controlgroup", required_argument, NULL, O_CONTROLGRP},
{"slaac", required_argument, NULL, O_SLAAC},
{"gateway", no_argument, NULL, O_GATEWAY},
{"nopfxdlg", no_argument, NULL, O_NOPFXDLG},
{"pfxdlgonly", no_argument, NULL, O_PFXDLGONLY},
{"ia_pd_mix", no_argument, NULL, O_PFXDLGMIX},
{NULL, 0, NULL, '\0'}
};
@ -1900,11 +1898,8 @@ err_sla:
else
ifo->options &= ~DHCPCD_SLAACPRIVATE;
break;
case O_NOPFXDLG:
ifo->options |= DHCPCD_NOPFXDLG;
break;
case O_PFXDLGONLY:
ifo->options |= DHCPCD_PFXDLGONLY;
case O_PFXDLGMIX:
ifo->options |= DHCPCD_PFXDLGMIX;
break;
default:
return 0;

View File

@ -105,6 +105,7 @@
#define DHCPCD_DHCP6 (1ULL << 50)
#define DHCPCD_NOPFXDLG (1ULL << 51)
#define DHCPCD_PFXDLGONLY (1ULL << 52)
#define DHCPCD_PFXDLGMIX (1ULL << 53)
extern const struct option cf_options[];

5
if.c
View File

@ -240,6 +240,7 @@ if_discover(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
}
if (ifp)
continue;
if (argc > 0) {
for (i = 0; i < argc; i++) {
#ifdef __linux__
@ -483,7 +484,9 @@ if_find(struct dhcpcd_ctx *ctx, const char *ifname)
if (ctx != NULL && ctx->ifaces != NULL) {
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (strcmp(ifp->name, ifname) == 0)
if ((ifp->options ||
!(ifp->options->options & DHCPCD_PFXDLGONLY)) &&
strcmp(ifp->name, ifname) == 0)
return ifp;
}
}