]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
When requesting a IA_PD and another IA type, create a psuedo interface
authorRoy Marples <roy@marples.name>
Mon, 7 Jul 2014 14:41:18 +0000 (14:41 +0000)
committerRoy Marples <roy@marples.name>
Mon, 7 Jul 2014 14:41:18 +0000 (14:41 +0000)
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.

dhcp6.c
dhcpcd.8.in
dhcpcd.c
dhcpcd.conf.5.in
dhcpcd.h
if-options.c
if-options.h
if.c

diff --git a/dhcp6.c b/dhcp6.c
index 1095bbe9ae9b7a85aa5ae46b0a3e1696b6320890..7dab3712399086ddeb2f0b3a3b1b7c40132bd666 100644 (file)
--- 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);
 
index 712f1525293d40c3f931a0905648fb99f1fc1593..c8e1d0d74b0d6e0b6dfb91a00345e09dbc4536f7 100644 (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
 .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
index 5d71d3a8bf5406da440cb60b1523793ab9c22ae9..4387f0cfee87116c9e39550856e2c09f52e9da5c 100644 (file)
--- 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))
index c509869434d9a9b59aecf79aef327810cf8ea85b..305bedcc3ef0d6ccc0ff2bd555cd0699687d4992 100644 (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
index 7371b602d150c597c02d7d2fd55ce2edb804b052..3aa59aed0fc0a89a44bbea73fa098acc0f1e8544 100644 (file)
--- 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
index c7f719bd8f16e9f24bbc019cbec9707365612aa2..4fa9069b2daa45e82e3b0e4947f3307042875e48 100644 (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;
index 39485e4d5882e7ba1656672087805753b4146ab4..a73f7851ec49de0bfa2bc767f1dd8cf47cae7e7f 100644 (file)
 #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 9deb6dd1eddfe93794b745afd1266945e6f39645..bcabca0e1cee08ddc98fff58b4512df19b0aedf2 100644 (file)
--- 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;
                }
        }