From: Roy Marples Date: Mon, 7 Jul 2014 14:41:18 +0000 (+0000) Subject: When requesting a IA_PD and another IA type, create a psuedo interface X-Git-Tag: v6.4.1~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=9d5cb9f924a239a6a0981d50c1e85f9b1d9ca36b;p=thirdparty%2Fdhcpcd.git 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. --- diff --git a/dhcp6.c b/dhcp6.c index 1095bbe9..7dab3712 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -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); diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 712f1525..c8e1d0d7 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -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 diff --git a/dhcpcd.c b/dhcpcd.c index 5d71d3a8..4387f0cf 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -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)) diff --git a/dhcpcd.conf.5.in b/dhcpcd.conf.5.in index c5098694..305bedcc 100644 --- a/dhcpcd.conf.5.in +++ b/dhcpcd.conf.5.in @@ -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 diff --git a/dhcpcd.h b/dhcpcd.h index 7371b602..3aa59aed 100644 --- a/dhcpcd.h +++ b/dhcpcd.h @@ -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 diff --git a/if-options.c b/if-options.c index c7f719bd..4fa9069b 100644 --- a/if-options.c +++ b/if-options.c @@ -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; diff --git a/if-options.h b/if-options.h index 39485e4d..a73f7851 100644 --- a/if-options.h +++ b/if-options.h @@ -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[]; diff --git a/if.c b/if.c index 9deb6dd1..bcabca0e 100644 --- a/if.c +++ b/if.c @@ -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; } }