changeset 2580:2f67f9e4d8cd draft

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.
author Roy Marples <roy@marples.name>
date Mon, 07 Jul 2014 14:41:18 +0000
parents e8cfd1740182
children 7902a620e627
files dhcp6.c dhcpcd.8.in dhcpcd.c dhcpcd.conf.5.in dhcpcd.h if-options.c if-options.h if.c
diffstat 8 files changed, 145 insertions(+), 81 deletions(-) [+]
line wrap: on
line diff
--- a/dhcp6.c	Mon Jul 07 10:01:24 2014 +0000
+++ b/dhcp6.c	Mon Jul 07 14:41:18 2014 +0000
@@ -2221,6 +2221,8 @@
 	}
 
 	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 @@
 	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 @@
 	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 @@
 		    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 @@
 	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 @@
 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);
 
--- a/dhcpcd.8.in	Mon Jul 07 10:01:24 2014 +0000
+++ b/dhcpcd.8.in	Mon Jul 07 14:41:18 2014 +0000
@@ -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 @@
 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 @@
 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
--- a/dhcpcd.c	Mon Jul 07 10:01:24 2014 +0000
+++ b/dhcpcd.c	Mon Jul 07 14:41:18 2014 +0000
@@ -581,7 +581,7 @@
 	}
 
 	/* 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 @@
 }
 
 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 @@
 	}
 }
 
+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 @@
 			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 @@
 			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 @@
 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 @@
 		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 @@
 			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 @@
 			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 @@
 			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 @@
 			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 @@
 			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 @@
 	}
 
 #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 @@
 	}
 
 
-	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 @@
 	}
 
 	TAILQ_FOREACH(ifp, ctx.ifaces, next) {
-		init_state(ifp, argc, argv);
+		dhcpcd_initstate1(ifp, argc, argv);
 	}
 
 	if (ctx.options & DHCPCD_BACKGROUND && dhcpcd_daemonise(&ctx))
--- a/dhcpcd.conf.5.in	Mon Jul 07 10:01:24 2014 +0000
+++ b/dhcpcd.conf.5.in	Mon Jul 07 14:41:18 2014 +0000
@@ -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 @@
 .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 @@
 .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
--- a/dhcpcd.h	Mon Jul 07 10:01:24 2014 +0000
+++ b/dhcpcd.h	Mon Jul 07 14:41:18 2014 +0000
@@ -151,5 +151,6 @@
 int dhcpcd_selectprofile(struct interface *, const char *);
 
 void dhcpcd_startinterface(void *);
+void dhcpcd_initstate(struct interface *);
 
 #endif
--- a/if-options.c	Mon Jul 07 10:01:24 2014 +0000
+++ b/if-options.c	Mon Jul 07 14:41:18 2014 +0000
@@ -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 @@
 	{"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 @@
 		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;
--- a/if-options.h	Mon Jul 07 10:01:24 2014 +0000
+++ b/if-options.h	Mon Jul 07 14:41:18 2014 +0000
@@ -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[];
 
--- a/if.c	Mon Jul 07 10:01:24 2014 +0000
+++ b/if.c	Mon Jul 07 14:41:18 2014 +0000
@@ -240,6 +240,7 @@
 		}
 		if (ifp)
 			continue;
+
 		if (argc > 0) {
 			for (i = 0; i < argc; i++) {
 #ifdef __linux__
@@ -483,7 +484,9 @@
 
 	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;
 		}
 	}