blob: 47240b1799849166c4a3008f4b92dc7747797127 [file] [log] [blame]
From 961c082f70fa51c2110b176d821bf92d198c01d0 Mon Sep 17 00:00:00 2001
From: Peter Qiu <zqiu@chromium.org>
Date: Fri, 29 May 2015 09:44:36 -0700
Subject: [PATCH] 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.
---
dbus/rpc-dbus.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
dhcp-common.c | 2 +-
dhcp6.c | 12 +++++++
dhcpcd.conf | 3 ++
if-options.c | 39 ++++++++++++++++++++
if-options.h | 2 +-
ipv6.c | 6 ++++
rpc-interface.h | 2 ++
rpc-stub.c | 2 ++
9 files changed, 174 insertions(+), 3 deletions(-)
diff --git a/dbus/rpc-dbus.c b/dbus/rpc-dbus.c
index cc88356..ea380c4 100644
--- a/dbus/rpc-dbus.c
+++ b/dbus/rpc-dbus.c
@@ -39,6 +39,9 @@
#include "../config.h"
#include "../eloop.h"
#include "../dhcp.h"
+#ifdef INET6
+#include "../dhcp6.h"
+#endif
#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 }
};
@@ -346,6 +366,83 @@ 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;
+
+ syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name);
+
+ msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event");
+ if (msg == NULL) {
+ syslog(LOG_ERR, "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) {
+ logger(dhcpcd_ctx, LOG_ERR,
+ "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) {
+ logger(dhcpcd_ctx, LOG_ERR,
+ "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)
+ syslog(LOG_ERR, "failed to send dhcpv6 to dbus");
+ } else
+ syslog(LOG_ERR, "failed to construct dbus message");
+ dbus_message_unref(msg);
+
+ return success;
+}
+#endif
+
static DBusHandlerResult
introspect(DBusConnection *con, DBusMessage *msg)
{
@@ -640,12 +737,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/dhcp-common.c b/dhcp-common.c
index 70e76ce..b0a2012 100644
--- a/dhcp-common.c
+++ b/dhcp-common.c
@@ -753,7 +753,7 @@ dhcp_set_leasefile(char *leasefile, size_t len, int family,
ifp->lease_identifier, "", "");
}
return snprintf(leasefile, len,
- family == AF_INET ? LEASEFILE : LEASEFILE,
+ family == AF_INET ? LEASEFILE : LEASEFILE6,
ifp->name, "", "");
}
diff --git a/dhcp6.c b/dhcp6.c
index 9b00b40..79b7cf1 100644
--- a/dhcp6.c
+++ b/dhcp6.c
@@ -50,6 +50,7 @@
#include "if.h"
#include "if-options.h"
#include "ipv6nd.h"
+#include "rpc-interface.h"
#include "script.h"
#ifndef __UNCONST
@@ -1373,6 +1374,7 @@ dhcp6_startdiscover(void *arg)
struct interface *ifp;
struct dhcp6_state *state;
+ rpc_signal_status("Discover6");
ifp = arg;
dhcp6_delete_delegates(ifp);
logger(ifp->ctx, LOG_INFO, "%s: soliciting a DHCPv6 lease", ifp->name);
@@ -1474,6 +1476,7 @@ dhcp6_startrebind(void *arg)
struct dhcp6_state *state;
int pd;
+ rpc_signal_status("Rebind6");
ifp = arg;
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp);
state = D6_STATE(ifp);
@@ -1516,6 +1519,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;
@@ -1539,6 +1543,7 @@ dhcp6_startconfirm(struct interface *ifp)
{
struct dhcp6_state *state;
+ rpc_signal_status("Confirm6");
state = D6_STATE(ifp);
state->state = DH6S_CONFIRM;
state->start_uptime = uptime();
@@ -1566,6 +1571,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)
@@ -1591,6 +1597,7 @@ dhcp6_startexpire(void *arg)
{
struct interface *ifp;
+ rpc_signal_status("Expire6");
ifp = arg;
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);
@@ -1614,6 +1621,7 @@ dhcp6_startrelease(struct interface *ifp)
if (state->state != DH6S_BOUND)
return;
+ rpc_signal_status("Release6");
state->state = DH6S_RELEASE;
state->start_uptime = uptime();
state->RTC = 0;
@@ -3039,9 +3047,13 @@ recv:
eloop_timeout_add_sec(ifp->ctx->eloop,
(time_t)state->expire, dhcp6_startexpire, ifp);
+#ifndef PASSIVE_MODE
ipv6nd_runignoredra(ifp);
ipv6_addaddrs(&state->addrs);
dhcp6_delegate_prefix(ifp);
+#else
+ rpc_update_ipv6(ifp);
+#endif /* PASSIVE_MODE */
if (state->state == DH6S_INFORMED)
logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
diff --git a/dhcpcd.conf b/dhcpcd.conf
index 3837feb..833c5b6 100644
--- a/dhcpcd.conf
+++ b/dhcpcd.conf
@@ -47,3 +47,6 @@ require dhcp_server_identifier
# A hook script is provided to lookup the hostname if not set by the DHCP
# server, but it should not be run by default.
#nohook lookup-hostname
+
+# Disable IPv6 router solicitation
+noipv6rs
diff --git a/if-options.c b/if-options.c
index 8680b5b..5d32811 100644
--- a/if-options.c
+++ b/if-options.c
@@ -102,6 +102,7 @@
#define O_BOOTP O_BASE + 42
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'},
@@ -671,6 +672,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) {
+ logger(ctx, LOG_ERR, "%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/if-options.h b/if-options.h
index f1c45b8..78c4358 100644
--- a/if-options.h
+++ b/if-options.h
@@ -41,7 +41,7 @@
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
-#define IF_OPTS "46bc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \
+#define IF_OPTS "46abc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \
"ABC:DEF:GHI:JKLMO:PQ:RS:TUVW:X:Z:"
#define DEFAULT_TIMEOUT 30
diff --git a/ipv6.c b/ipv6.c
index 66c2fca..7fe3c32 100644
--- a/ipv6.c
+++ b/ipv6.c
@@ -609,6 +609,7 @@ ipv6_checkaddrflags(void *arg)
static void
ipv6_deleteaddr(struct ipv6_addr *ia)
{
+#ifndef PASSIVE_MODE
struct ipv6_state *state;
struct ipv6_addr *ap;
@@ -626,11 +627,13 @@ ipv6_deleteaddr(struct ipv6_addr *ia)
break;
}
}
+#endif
}
int
ipv6_addaddr(struct ipv6_addr *ap, const struct timespec *now)
{
+#ifndef PASSIVE_MODE
struct interface *ifp;
struct ipv6_state *state;
struct ipv6_addr *nap;
@@ -741,6 +744,7 @@ ipv6_addaddr(struct ipv6_addr *ap, const struct timespec *now)
&tv, ipv6_checkaddrflags, ap);
}
#endif
+#endif
return 0;
}
@@ -2016,6 +2020,7 @@ ipv6_build_dhcp_routes(struct dhcpcd_ctx *ctx,
void
ipv6_buildroutes(struct dhcpcd_ctx *ctx)
{
+#ifndef PASSIVE_MODE
struct rt6_head dnr, *nrs;
struct rt6 *rt, *rtn, *or;
uint8_t have_default;
@@ -2119,4 +2124,5 @@ ipv6_buildroutes(struct dhcpcd_ctx *ctx)
free(ctx->ipv6->routes);
ctx->ipv6->routes = nrs;
+#endif
}
diff --git a/rpc-interface.h b/rpc-interface.h
index 6e1e7e0..cf4da46 100644
--- a/rpc-interface.h
+++ b/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/rpc-stub.c b/rpc-stub.c
index aa65097..e2b4ed9 100644
--- a/rpc-stub.c
+++ b/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.2.0.rc0.207.ga3a616c