blob: 43f2afb514d233c24af5ddbaf2f549d1bc965555 [file] [log] [blame]
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