| From 63435f1d3f36c4ffc0375789d70e27277927308e Mon Sep 17 00:00:00 2001 |
| From: Peter Qiu <zqiu@chromium.org> |
| Date: Mon, 14 Jun 2021 11:50:10 +0000 |
| Subject: [PATCH 14/19] ChromiumOS DHCPv6 support |
| |
| Add support to emit DHCPv6 configuration/status updates through |
| DBus, and prevent the daemon from updating IPv6 system configurations. |
| |
| Add command line option "-a" to enable ia options for shill usage, |
| since ia options can only be specified through the config file by |
| default. When "-a" option is enabled, the daemon will request ia_na |
| (Non-temporary Address) and ia_pd (Prefix Delegation) options from |
| the DHCPv6 server. |
| |
| BUG=chromium:496751 |
| TEST=Verify using upcoming client test network_Dhcpv6* |
| Manual Test: |
| 1. Create a virtual ethernet pair and setup a DHCPv6 server on the |
| master interface (veth_master) using dhcpd: |
| "dhcpd -6 -d -cf /tmp/dhcp6.conf veth_master" |
| with following config: |
| default-lease-time 600; |
| max-lease-time 7200; |
| subnet6 2001:db8:0:1::/64 { |
| # Range for clients |
| range6 2001:db8:0:1::129 2001:db8:0:1::254; |
| # Additional options |
| option dhcp6.name-servers fec0:0:0:1::1; |
| option dhcp6.domain-search "domain.example"; |
| # Prefix range for delegation to sub-routers |
| prefix6 2001:db8:0:100:: 2001:db8:0:f00:: /56; |
| } |
| 2. Start DBus monitor using "dbus-monitor --system" |
| 3. Start a DHCPv6 client on the slave interface using dhcpcd: |
| "dhcpcd -6 -B -a veth_slave" |
| 4. Verify DHCPv6 configurations through DBus messages. |
| --- |
| src/dbus/rpc-dbus.c | 107 +++++++++++++++++++++++++++++++++++++++++++- |
| src/dhcp6.c | 11 +++++ |
| src/dhcpcd.conf | 3 ++ |
| src/if-options.c | 39 ++++++++++++++++ |
| src/if-options.h | 2 +- |
| src/rpc-interface.h | 2 + |
| src/rpc-stub.c | 2 + |
| 7 files changed, 164 insertions(+), 2 deletions(-) |
| |
| diff --git a/src/dbus/rpc-dbus.c b/src/dbus/rpc-dbus.c |
| index bea71b22..4298d4c2 100644 |
| --- a/src/dbus/rpc-dbus.c |
| +++ b/src/dbus/rpc-dbus.c |
| @@ -38,6 +38,9 @@ |
| #include "../config.h" |
| #include "../eloop.h" |
| #include "../dhcp.h" |
| +#ifdef INET6 |
| +#include "../dhcp6.h" |
| +#endif |
| #include "../logerr.h" |
| #include "../rpc-interface.h" |
| #include "dbus-dict.h" |
| @@ -217,6 +220,23 @@ static const struct o_dbus dhos[] = { |
| { "domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| "DomainSearch" }, |
| { "wpad_url=", DBUS_TYPE_STRING, 0, "WebProxyAutoDiscoveryUrl" }, |
| +#ifdef INET6 |
| + { "dhcp6_server_id=", DBUS_TYPE_STRING, 0, |
| + "DHCPv6ServerIdentifier" }, |
| + { "dhcp6_ia_na1_ia_addr1=", DBUS_TYPE_STRING, 0, "DHCPv6Address" }, |
| + { "dhcp6_ia_na1_ia_addr1_vltime=", DBUS_TYPE_UINT32, 0, |
| + "DHCPv6AddressLeaseTime" }, |
| + { "dhcp6_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| + "DHCPv6NameServers" }, |
| + { "dhcp6_domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, |
| + "DHCPv6DomainSearch" }, |
| + { "dhcp6_ia_pd1_prefix1=", DBUS_TYPE_STRING, 0, |
| + "DHCPv6DelegatedPrefix" }, |
| + { "dhcp6_ia_pd1_prefix1_length=", DBUS_TYPE_UINT32, 0, |
| + "DHCPv6DelegatedPrefixLength" }, |
| + { "dhcp6_ia_pd1_prefix1_vltime=", DBUS_TYPE_UINT32, 0, |
| + "DHCPv6DelegatedPrefixLeaseTime" }, |
| +#endif |
| { NULL, 0, 0, NULL } |
| }; |
| |
| @@ -344,6 +364,81 @@ dbus_send_message(const struct interface *ifp, const char *reason, |
| return success; |
| } |
| |
| +#ifdef INET6 |
| +static dbus_bool_t |
| +dbus_send_dhcpv6_message(const struct interface *ifp, const char *reason, |
| + const char *prefix, struct dhcp6_message *message, size_t length) |
| +{ |
| + const struct if_options *ifo = ifp->options; |
| + DBusMessage* msg; |
| + DBusMessageIter args, dict; |
| + int pid = getpid(); |
| + char **env = NULL; |
| + ssize_t e, elen; |
| + int retval; |
| + int success = FALSE; |
| + |
| + loginfox("event %s on interface %s", reason, ifp->name); |
| + |
| + msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event"); |
| + if (msg == NULL) { |
| + logerrx("failed to make a configure message"); |
| + return FALSE; |
| + } |
| + dbus_message_iter_init_append(msg, &args); |
| + dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); |
| + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason); |
| + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, |
| + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING |
| + DBUS_TYPE_STRING_AS_STRING |
| + DBUS_TYPE_VARIANT_AS_STRING |
| + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, |
| + &dict); |
| + if (prefix == NULL || message == NULL) |
| + retval = 0; |
| + else { |
| + e = dhcp6_env(NULL, NULL, ifp, message, length); |
| + if (e > 0) { |
| + char *config_prefix = strdup(prefix); |
| + if (config_prefix == NULL) { |
| + logerrx("Memory exhausted (strdup)"); |
| + eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| + } |
| + char *p = config_prefix + strlen(config_prefix) - 1; |
| + if (p >= config_prefix && *p == '_') |
| + *p = '\0'; |
| + env = calloc(e + 1, sizeof(char *)); |
| + if (env == NULL) { |
| + logerrx("Memory exhausted (calloc)"); |
| + eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); |
| + } |
| + elen = dhcp6_env(env, "new", ifp, message, length); |
| + free(config_prefix); |
| + } |
| + retval = append_config(&dict, prefix, env, elen); |
| + } |
| + |
| + /* Release memory allocated for env. */ |
| + if (env) { |
| + char **current = env; |
| + while (*current) |
| + free(*current++); |
| + free(env); |
| + } |
| + |
| + dbus_message_iter_close_container(&args, &dict); |
| + if (retval == 0) { |
| + success = dbus_connection_send(connection, msg, NULL); |
| + if (!success) |
| + logerrx("failed to send dhcpv6 to dbus"); |
| + } else |
| + logerrx("failed to construct dbus message"); |
| + dbus_message_unref(msg); |
| + |
| + return success; |
| +} |
| +#endif |
| + |
| static DBusHandlerResult |
| introspect(DBusConnection *con, DBusMessage *msg) |
| { |
| @@ -634,12 +729,22 @@ rpc_update_ipv4(struct interface *ifp) |
| return 0; |
| } |
| |
| +#ifdef INET6 |
| int |
| rpc_update_ipv6(struct interface *ifp) |
| { |
| - /* Currently not supported. */ |
| + struct dhcp6_state *state = D6_STATE(ifp); |
| + if (state->new != NULL) { |
| + /* push state over d-bus */ |
| + dbus_send_dhcpv6_message(ifp, state->reason, "new_", |
| + state->new, state->new_len); |
| + rpc_signal_status("Bound6"); |
| + } else { |
| + rpc_signal_status("Release6"); |
| + } |
| return 0; |
| } |
| +#endif |
| |
| int |
| rpc_notify_unicast_arp(struct interface *ifp) { |
| diff --git a/src/dhcp6.c b/src/dhcp6.c |
| index aebb5224..d84a8d20 100644 |
| --- a/src/dhcp6.c |
| +++ b/src/dhcp6.c |
| @@ -55,6 +55,7 @@ |
| #include "if-options.h" |
| #include "ipv6nd.h" |
| #include "logerr.h" |
| +#include "rpc-interface.h" |
| #include "script.h" |
| |
| #ifdef HAVE_SYS_BITOPS_H |
| @@ -1563,6 +1564,7 @@ dhcp6_startdiscover(void *arg) |
| struct interface *ifp; |
| struct dhcp6_state *state; |
| |
| + rpc_signal_status("Discover6"); |
| ifp = arg; |
| #ifndef SMALL |
| dhcp6_delete_delegates(ifp); |
| @@ -1593,6 +1595,7 @@ dhcp6_startinform(void *arg) |
| struct interface *ifp; |
| struct dhcp6_state *state; |
| |
| + rpc_signal_status("Inform6"); |
| ifp = arg; |
| state = D6_STATE(ifp); |
| if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG) |
| @@ -1735,6 +1738,7 @@ dhcp6_startrebind(void *arg) |
| int pd; |
| #endif |
| |
| + rpc_signal_status("Rebind6"); |
| ifp = arg; |
| eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp); |
| state = D6_STATE(ifp); |
| @@ -1780,6 +1784,7 @@ dhcp6_startrequest(struct interface *ifp) |
| { |
| struct dhcp6_state *state; |
| |
| + rpc_signal_status("Request6"); |
| eloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp); |
| state = D6_STATE(ifp); |
| state->state = DH6S_REQUEST; |
| @@ -1803,6 +1808,7 @@ dhcp6_startconfirm(struct interface *ifp) |
| { |
| struct dhcp6_state *state; |
| |
| + rpc_signal_status("Confirm6"); |
| state = D6_STATE(ifp); |
| state->state = DH6S_CONFIRM; |
| state->RTC = 0; |
| @@ -1841,6 +1847,7 @@ dhcp6_startexpire(void *arg) |
| { |
| struct interface *ifp; |
| |
| + rpc_signal_status("Expire6"); |
| ifp = arg; |
| eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp); |
| |
| @@ -1887,6 +1894,7 @@ dhcp6_startrelease(struct interface *ifp) |
| if (state->state != DH6S_BOUND) |
| return; |
| |
| + rpc_signal_status("Release6"); |
| state->state = DH6S_RELEASE; |
| state->RTC = 0; |
| state->IMD = REL_MAX_DELAY; |
| @@ -3185,6 +3193,9 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom) |
| |
| ipv6_addaddrs(&state->addrs); |
| dhcp6_deprecateaddrs(&state->addrs); |
| +#ifdef PASSIVE_MODE |
| + rpc_update_ipv6(ifp); |
| +#endif |
| |
| if (state->state == DH6S_INFORMED) |
| lognewinfo("%s: refresh in %"PRIu32" seconds", |
| diff --git a/src/dhcpcd.conf b/src/dhcpcd.conf |
| index d7ba8c9a..f4fe9b18 100644 |
| --- a/src/dhcpcd.conf |
| +++ b/src/dhcpcd.conf |
| @@ -45,3 +45,6 @@ require dhcp_server_identifier |
| #slaac hwaddr |
| # OR generate Stable Private IPv6 Addresses based from the DUID |
| #slaac private |
| + |
| +# Disable IPv6 router solicitation |
| +noipv6rs |
| diff --git a/src/if-options.c b/src/if-options.c |
| index 1ced9642..edcda8fd 100644 |
| --- a/src/if-options.c |
| +++ b/src/if-options.c |
| @@ -108,6 +108,7 @@ |
| #define O_UNICASTGW O_BASE + 49 |
| |
| const struct option cf_options[] = { |
| + {"shill-ipv6", no_argument, NULL, 'a'}, |
| {"background", no_argument, NULL, 'b'}, |
| {"script", required_argument, NULL, 'c'}, |
| {"debug", no_argument, NULL, 'd'}, |
| @@ -717,6 +718,44 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, |
| case 'U': /* FALLTHROUGH */ |
| case 'V': /* We need to handle non interface options */ |
| break; |
| +#ifdef INET6 |
| + case 'a': |
| + /* Chromeos hack: configure DHCPv6 option for shill. */ |
| + |
| + /* Reallocate ia to add both ia_na and ia_pd. */ |
| + ia = realloc(ifo->ia, sizeof(*ifo->ia) * (ifo->ia_len + 2)); |
| + if (ia == NULL) { |
| + logerr("%s: %m", __func__); |
| + return -1; |
| + } |
| + ifo->ia = ia; |
| + |
| + /* Setup ia_na option with iaid of 0. */ |
| + ia = &ifo->ia[ifo->ia_len++]; |
| + ia->ia_type = D6_OPTION_IA_NA; |
| + parse_iaid(ia->iaid, "0", sizeof(ia->iaid)); |
| + ia->iaid_set = 1; |
| + memset(&ia->addr, 0, sizeof(ia->addr)); |
| + ia->prefix_len = 0; |
| + ia->sla_max = 0; |
| + ia->sla_len = 0; |
| + ia->sla = NULL; |
| + |
| + /* Setup ia_pd option with iaid of 1. */ |
| + ia = &ifo->ia[ifo->ia_len++]; |
| + ia->ia_type = D6_OPTION_IA_PD; |
| + parse_iaid(ia->iaid, "1", sizeof(ia->iaid)); |
| + ia->iaid_set = 1; |
| + memset(&ia->addr, 0, sizeof(ia->addr)); |
| + ia->prefix_len = 0; |
| + ia->sla_max = 0; |
| + ia->sla_len = 0; |
| + ia->sla = NULL; |
| + |
| + /* Enable ia option. */ |
| + ifo->options |= DHCPCD_IA_FORCED; |
| + break; |
| +#endif |
| case 'b': |
| ifo->options |= DHCPCD_BACKGROUND; |
| break; |
| diff --git a/src/if-options.h b/src/if-options.h |
| index 3ef02c2e..1dae344f 100644 |
| --- a/src/if-options.h |
| +++ b/src/if-options.h |
| @@ -42,7 +42,7 @@ |
| |
| /* Don't set any optional arguments here so we retain POSIX |
| * compatibility with getopt */ |
| -#define IF_OPTS "146bc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \ |
| +#define IF_OPTS "146abc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \ |
| "ABC:DEF:GHI:JKLMNO:PQ:RS:TUVW:X:Z:" |
| #define NOERR_IF_OPTS ":" IF_OPTS |
| |
| diff --git a/src/rpc-interface.h b/src/rpc-interface.h |
| index 6e1e7e0c..cf4da466 100644 |
| --- a/src/rpc-interface.h |
| +++ b/src/rpc-interface.h |
| @@ -42,8 +42,10 @@ void rpc_signal_status(const char *); |
| /* Update IPv4 configuration. Return 0 on success. */ |
| int rpc_update_ipv4(struct interface *ifp); |
| |
| +#ifdef INET6 |
| /* Update IPv6 configuration. Return 0 on success. */ |
| int rpc_update_ipv6(struct interface *ifp); |
| +#endif |
| |
| /* Emit notification for successful unicast ARP. Return 0 on success. */ |
| int rpc_notify_unicast_arp(struct interface *ifp); |
| diff --git a/src/rpc-stub.c b/src/rpc-stub.c |
| index aa650974..e2b4ed90 100644 |
| --- a/src/rpc-stub.c |
| +++ b/src/rpc-stub.c |
| @@ -53,12 +53,14 @@ rpc_update_ipv4(struct interface *ifp) |
| return 0; |
| } |
| |
| +#ifdef INET6 |
| int |
| rpc_update_ipv6(struct interface *ifp) |
| { |
| /* Stub implementation. */ |
| return 0; |
| } |
| +#endif |
| |
| int |
| rpc_notify_unicast_arp(struct interface *ifp) |
| -- |
| 2.33.0.800.g4c38ced690-goog |
| |