| 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 |
| |