From patchwork Mon Sep 19 13:30:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980448 Received: from mail-wm1-f51.google.com (mail-wm1-f51.google.com [209.85.128.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C7964C79 for ; Mon, 19 Sep 2022 13:31:11 +0000 (UTC) Received: by mail-wm1-f51.google.com with SMTP id ay7-20020a05600c1e0700b003b49861bf48so3868761wmb.0 for ; Mon, 19 Sep 2022 06:31:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date; bh=7tbpeBZOcBh9smpgVIA0WOBnMrzIAfe4ghmKBWYjmMY=; b=PO61Hkdk5X8vC1zpMswCT4E4mymYjab9OLQtvEGcyEDjoo2WYOM3syGmY9PRziF9MA 1j+l6oS3YNksaoY9m/uKtm9MgBUrgJENRig15MXT1UYzwKr/3F742ge/80dHFyp3DvNn RqzpgX7qXR/kw08BUX9HuQsKj0IL322X6KdvLoNqn2a6eDAuEWD7T9GWl+mcvolC4XCJ mppBbOMnTVjnADdqaK2TsT1T3LCMYS/8xGluRD0m2JDaL13ntWw4Fad1cQOqcmXipARU m3VA/0Mrl5KkvD1KG3D+KTGtYiTWWc1j6O4BvDbQLPkHIOOZT3YiuHzwPz2IeUDKhMI+ J8fQ== X-Gm-Message-State: ACrzQf3JNbaAPeDB7Zc70d0jD1gss8Xog/1MaR6u++pzE8Wd1IEugVHs YFdhDmRanSGsdU64eDcUzrCWxmVFyraQ1g== X-Google-Smtp-Source: AMsMyM4UX1ub9e9E3NKGtzZllYLENoIe4xYAgtcxWmSib6P7NRsabiiTEN2ClXMflVKz+rAgh9p36g== X-Received: by 2002:a05:600c:5d3:b0:3b4:c326:d096 with SMTP id p19-20020a05600c05d300b003b4c326d096mr8112638wmd.98.1663594269407; Mon, 19 Sep 2022 06:31:09 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.08 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:08 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Date: Mon, 19 Sep 2022 15:30:59 +0200 Message-Id: <20220919133105.3129080-1-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In struct l_icmp6_router save the lists of prefixes for autoconfiguration separately from routes. The two sets of prefixes may overlap completely, partially or not at all. Drop the preferred_lifetime information from route_info since this is strictly to be used as the preferred lifetime for addresses generated through SLAAC and has nothing to do with routes. --- ell/icmp6-private.h | 7 ++++ ell/icmp6.c | 98 +++++++++++++++++++++++++++++++-------------- ell/netconfig.c | 7 +--- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/ell/icmp6-private.h b/ell/icmp6-private.h index 77df0b8..a26639d 100644 --- a/ell/icmp6-private.h +++ b/ell/icmp6-private.h @@ -25,6 +25,11 @@ struct route_info { bool onlink : 1; uint8_t prefix_len; uint8_t preference; + uint32_t valid_lifetime; +}; + +struct autoconf_prefix_info { + uint8_t prefix[8]; uint32_t preferred_lifetime; uint32_t valid_lifetime; }; @@ -40,6 +45,8 @@ struct l_icmp6_router { uint32_t max_rtr_adv_interval_ms; uint32_t n_routes; struct route_info *routes; + uint32_t n_ac_prefixes; + struct autoconf_prefix_info *ac_prefixes; }; struct l_icmp6_router *_icmp6_router_new(); diff --git a/ell/icmp6.c b/ell/icmp6.c index 7319903..b71a98f 100644 --- a/ell/icmp6.c +++ b/ell/icmp6.c @@ -693,9 +693,58 @@ struct l_icmp6_router *_icmp6_router_new() void _icmp6_router_free(struct l_icmp6_router *r) { l_free(r->routes); + l_free(r->ac_prefixes); l_free(r); } +/* Note: the following two write to @out even when they return false */ +static bool icmp6_prefix_parse_rt_info(const uint8_t *data, + struct route_info *out) +{ + out->prefix_len = data[2]; + out->onlink = true; + out->preference = 0; + out->valid_lifetime = l_get_be32(data + 4); + + /* + * Only the initial Prefix Length bits of the prefix are valid. + * The remaining bits "MUST" be ignored by the receiver. + */ + memcpy(out->address, net_prefix_from_ipv6(data + 16, out->prefix_len), + 16); + + if (out->prefix_len >= 10 && IN6_IS_ADDR_LINKLOCAL(out->address)) + return false; + + return true; +} + +static bool icmp6_prefix_parse_ac_info(const uint8_t *data, + struct autoconf_prefix_info *out) +{ + /* + * Per RFC4862 we need to silently ignore prefixes with a + * preferred lifetime longer than valid lifetime, those with + * 0 valid lifetime and those with link-local prefixes. + * Prefix Length must be 8 bytes (IPv6 address - Interface ID). + */ + if (data[2] != 64) + return false; + + if (IN6_IS_ADDR_LINKLOCAL(data + 16)) + return false; + + out->valid_lifetime = l_get_be32(data + 4); + out->preferred_lifetime = l_get_be32(data + 8); + + if (out->valid_lifetime == 0 || + out->preferred_lifetime > out->valid_lifetime) + return false; + + memcpy(out->prefix, data + 16, 8); + return true; +} + struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, size_t len, const uint8_t src[static 16], @@ -705,6 +754,7 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, const uint8_t *opts; uint32_t opts_len; uint32_t n_routes = 0; + uint32_t n_ac_prefixes = 0; if (ra->nd_ra_type != ND_ROUTER_ADVERT) return NULL; @@ -742,6 +792,10 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, if (opts[3] & ND_OPT_PI_FLAG_ONLINK) n_routes += 1; + + if (opts[3] & ND_OPT_PI_FLAG_AUTO) + n_ac_prefixes += 1; + break; case ND_OPT_ROUTE_INFORMATION: if (l < 8) @@ -781,6 +835,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, memcpy(r->address, src, sizeof(r->address)); r->routes = l_new(struct route_info, n_routes); r->n_routes = n_routes; + r->ac_prefixes = l_new(struct autoconf_prefix_info, n_ac_prefixes); + r->n_ac_prefixes = n_ac_prefixes; if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) r->managed = true; @@ -798,6 +854,7 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, opts = (uint8_t *) (ra + 1); opts_len = len - sizeof(struct nd_router_advert); n_routes = 0; + n_ac_prefixes = 0; while (opts_len) { uint8_t t = opts[0]; @@ -814,41 +871,22 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, break; case ND_OPT_PREFIX_INFORMATION: - { - struct route_info *i = &r->routes[n_routes]; - - if (!(opts[3] & ND_OPT_PI_FLAG_ONLINK)) - break; + if (opts[3] & ND_OPT_PI_FLAG_ONLINK) { + struct route_info *i = &r->routes[n_routes]; - i->prefix_len = opts[2]; - i->onlink = true; - i->valid_lifetime = l_get_be32(opts + 4); - i->preferred_lifetime = l_get_be32(opts + 8); - - /* - * Only the initial Prefix Length bits of the prefix - * are valid. The remaining bits "MUST" be ignored - * by the receiver. - */ - memcpy(i->address, net_prefix_from_ipv6(opts + 16, - i->prefix_len), 16); + if (icmp6_prefix_parse_rt_info(opts, i)) + n_routes++; + } - /* - * For SLAAC (RFC4862) we need to "silently ignore" - * routes with a preferred lifetime longer than valid - * lifetime, and those with the link-local prefix. - * Since it makes sense, do it regardless of SLAAC. - */ - if (i->preferred_lifetime > i->valid_lifetime) - break; + if (opts[3] & ND_OPT_PI_FLAG_AUTO) { + struct autoconf_prefix_info *i = + &r->ac_prefixes[n_ac_prefixes]; - if (i->prefix_len >= 10 && - IN6_IS_ADDR_LINKLOCAL(i->address)) - break; + if (icmp6_prefix_parse_ac_info(opts, i)) + n_ac_prefixes++; + } - n_routes += 1; break; - } case ND_OPT_RTR_ADV_INTERVAL: if (l < 8) break; diff --git a/ell/netconfig.c b/ell/netconfig.c index 29e95b7..bbe1dec 100644 --- a/ell/netconfig.c +++ b/ell/netconfig.c @@ -799,7 +799,6 @@ static bool netconfig_check_route_need_update( static void netconfig_set_icmp6_route_data(struct l_netconfig *nc, struct netconfig_route_data *rd, const struct l_icmp6_router *ra, - uint32_t preferred_lifetime, uint32_t valid_lifetime, uint32_t mtu, bool updated) { @@ -916,10 +915,10 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, * netconfig_set_icmp6_route_data. */ netconfig_set_icmp6_route_data(nc, default_rd, r, r->lifetime, - r->lifetime, r->mtu, false); + r->mtu, false); } else if (default_rd && r->lifetime) netconfig_set_icmp6_route_data(nc, default_rd, r, r->lifetime, - r->lifetime, r->mtu, true); + r->mtu, true); else if (default_rd && !r->lifetime) netconfig_remove_icmp6_route(nc, default_rd); @@ -941,12 +940,10 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, continue; netconfig_set_icmp6_route_data(nc, rd, r, - info->preferred_lifetime, info->valid_lifetime, gateway ? r->mtu : 0, false); } else if (rd && info->valid_lifetime) netconfig_set_icmp6_route_data(nc, rd, r, - info->preferred_lifetime, info->valid_lifetime, gateway ? r->mtu : 0, true); else if (rd && !info->valid_lifetime) From patchwork Mon Sep 19 13:31:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980449 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9983D4C7C for ; Mon, 19 Sep 2022 13:31:12 +0000 (UTC) Received: by mail-wr1-f44.google.com with SMTP id n10so16952174wrw.12 for ; Mon, 19 Sep 2022 06:31:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=3R9INdt9TpcZUy6qK3CoAew1LarerMroyK/3WMLXTO8=; b=I9EnSHO5z9bSWEyDOOLeihHswlALK+DBgAnwmj/6kOOOtHtEYdDF4qf/OoZJFKUw/5 SPN66y5yQLDEEu+Dcgf8TR/AZz74llqSGsLvLQDh7y+o8KaXASGUw9ogXtYy3Vm1sbL5 iI9l2LmsFuCTSaVmd2WUy1XbdbI1rmQyKAt5o8YdIBNMbyVRAoOH6jGSpTICIMxD8adJ Dk9RDCopzpnkoGEvTXV0MKNsuuf5DP0OA9iGHknT5b1L6YeE8xwZyXnep8vAY2jyIJRE RyOVXYM+URnXaW2VS+0c+0KO6T+P5ZcvY3VwSIp3e27rkt+Q0KNKgh+71d2NumtB+H3p ir/g== X-Gm-Message-State: ACrzQf2qQ8eOq/WW3AYM+LLbg6JKbr5HW3Q4Si2kLBANp9k8vJg1BQM8 hPyNtbCmhyjzHoE0mdH7Vs9gsGqmQOqFkQ== X-Google-Smtp-Source: AMsMyM5Hjp7ZxyP/8a2zg+Kptuy/utlsv6aoXLnQyM0gzsXeyoKkqPwMwOXLUcs0nuVsa+O9tTbkSg== X-Received: by 2002:a5d:47c5:0:b0:22a:6d4c:f21e with SMTP id o5-20020a5d47c5000000b0022a6d4cf21emr10814105wrc.417.1663594270521; Mon, 19 Sep 2022 06:31:10 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:10 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options Date: Mon, 19 Sep 2022 15:31:00 +0200 Message-Id: <20220919133105.3129080-2-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Save the list of DNS server addresses and the local domain search list from Router Advertisements in the l_icmp6_router structure. --- ell/icmp6-private.h | 14 +++++++ ell/icmp6.c | 94 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/ell/icmp6-private.h b/ell/icmp6-private.h index a26639d..f207164 100644 --- a/ell/icmp6-private.h +++ b/ell/icmp6-private.h @@ -34,6 +34,16 @@ struct autoconf_prefix_info { uint32_t valid_lifetime; }; +struct dns_info { + uint8_t address[16]; + uint32_t lifetime; +}; + +struct domain_info { + char *domain; + uint32_t lifetime; +}; + struct l_icmp6_router { uint8_t address[16]; bool managed : 1; @@ -47,6 +57,10 @@ struct l_icmp6_router { struct route_info *routes; uint32_t n_ac_prefixes; struct autoconf_prefix_info *ac_prefixes; + uint32_t n_dns; + struct dns_info *dns_list; + uint32_t n_domains; + struct domain_info *domains; }; struct l_icmp6_router *_icmp6_router_new(); diff --git a/ell/icmp6.c b/ell/icmp6.c index b71a98f..c38df17 100644 --- a/ell/icmp6.c +++ b/ell/icmp6.c @@ -50,6 +50,7 @@ #include "netlink.h" #include "rtnl.h" #include "missing.h" +#include "utf8.h" #include "icmp6.h" #include "icmp6-private.h" @@ -58,6 +59,14 @@ #define ND_OPT_ROUTE_INFORMATION 24 #endif +/* RFC8106 */ +#ifndef ND_OPT_RECURSIVE_DNS_SERVER +#define ND_OPT_RECURSIVE_DNS_SERVER 25 +#endif +#ifndef ND_OPT_DNS_SEARCH_LIST +#define ND_OPT_DNS_SEARCH_LIST 31 +#endif + #define CLIENT_DEBUG(fmt, args...) \ l_util_debug(client->debug_handler, client->debug_data, \ "%s:%i " fmt, __func__, __LINE__, ## args) @@ -755,6 +764,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, uint32_t opts_len; uint32_t n_routes = 0; uint32_t n_ac_prefixes = 0; + uint32_t n_dns = 0; + uint32_t n_domains = 0; if (ra->nd_ra_type != ND_ROUTER_ADVERT) return NULL; @@ -824,8 +835,48 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, break; n_routes += 1; + break; + case ND_OPT_RECURSIVE_DNS_SERVER: + if (l < 24 || (l & 15) != 8) + return NULL; + + n_dns += (l - 8) / 16; + break; + case ND_OPT_DNS_SEARCH_LIST: + { + unsigned int n_labels; + unsigned int pos = 8; + + if (l < 16) + return NULL; + + /* Count domains according to RFC1035 Section 3.1 */ + do { + unsigned int label_len; + + n_labels = 0; + + do { + label_len = opts[pos]; + pos += 1 + label_len; + n_labels += label_len ? 1 : 0; + } while (label_len && pos < l); + + /* + * Check if the root label was missing, or + * a label didn't fit in the option bytes, or + * the first domain had 0 labels, i.e. there + * were no domains. + */ + if (label_len || pos > l || pos == 9) + return NULL; + + n_domains += n_labels ? 1 : 0; + } while (n_labels && pos < l); + break; } + } opts += l; opts_len -= l; @@ -834,9 +885,9 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, r = _icmp6_router_new(); memcpy(r->address, src, sizeof(r->address)); r->routes = l_new(struct route_info, n_routes); - r->n_routes = n_routes; r->ac_prefixes = l_new(struct autoconf_prefix_info, n_ac_prefixes); - r->n_ac_prefixes = n_ac_prefixes; + r->dns_list = l_new(struct dns_info, n_dns); + r->domains = l_new(struct domain_info, n_domains); if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) r->managed = true; @@ -855,6 +906,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, opts_len = len - sizeof(struct nd_router_advert); n_routes = 0; n_ac_prefixes = 0; + n_dns = 0; + n_domains = 0; while (opts_len) { uint8_t t = opts[0]; @@ -946,12 +999,49 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra, n_routes += 1; break; } + case ND_OPT_RECURSIVE_DNS_SERVER: + { + unsigned int pos; + + for (pos = 8; pos < l; pos += 16) { + struct dns_info *i = &r->dns_list[n_dns++]; + + i->lifetime = l_get_be32(opts + 4); + memcpy(i->address, opts + pos, 16); + } + + break; + } + case ND_OPT_DNS_SEARCH_LIST: + { + struct domain_info *info = &r->domains[n_domains]; + _auto_(l_free) char **domain_list = + net_domain_list_parse(opts + 8, l - 8); + char **i; + + /* Ignore invalid option */ + if (!domain_list) + break; + + for (i = domain_list; *i; i++) { + info->lifetime = l_get_be32(opts + 4); + info->domain = *i; + info++; + n_domains++; + } + + break; + } } opts += l; opts_len -= l; } + r->n_routes = n_routes; + r->n_ac_prefixes = n_ac_prefixes; + r->n_dns = n_dns; + r->n_domains = n_domains; return r; } From patchwork Mon Sep 19 13:31:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980451 Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D63E14C79 for ; Mon, 19 Sep 2022 13:31:13 +0000 (UTC) Received: by mail-wr1-f53.google.com with SMTP id n10so16952285wrw.12 for ; Mon, 19 Sep 2022 06:31:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=H7jxvWWBX6+C16MQTakbhla6Q7eS935FTbw2fTJrUUw=; b=zhgbtR6lDhh0hF9AZlWx+aCl4MNQZOawWh3XdlPjFdNt3vn4qM/h9RXsC8tpFO/3u2 QB6XJ0wEc8K1F6uLt0nnrgm9tguSDwy/zrinwYBcWlYcj84rGHlbfZPTdioAwYFegp6v UK+nrbTdMTZFe44/RbE7nz28ucvcL1K5NqohomPBnCu9Ov2TFpiLi6a6v1LQ8IZoj4v5 pfttU5X7ZrG+K/veVAoRyYcnQMOnwyq4/1y3yA3TTm0ANoIxLfUPNVg6UG2d+pPZhpM2 QvXJUkZXjooiJWJ6+636otVUM1fQ1LJqwNDsA5jrqKRVDFweZuqDP9oWkn6PvMp2rKQV RFVg== X-Gm-Message-State: ACrzQf0e3rpMyN7GOIHmJFdshVsUnGYIYhhU9g2uZbF/VrdvhFX7CgkS Cg7scUhLjWXUb6ZBRFFd/0VhFKfL7YmBNQ== X-Google-Smtp-Source: AMsMyM4XajttosKGb1eqDM5cV8y97KsTYa4gOnVsu2CT5Tf725zm2MIrehWAAB7CeaD+7r6Br2kr6A== X-Received: by 2002:a5d:64ee:0:b0:226:db9b:e381 with SMTP id g14-20020a5d64ee000000b00226db9be381mr11185956wri.52.1663594271547; Mon, 19 Sep 2022 06:31:11 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.10 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:10 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 3/7] icmp6: Switch socket from AF_INET6 to AF_PACKET Date: Mon, 19 Sep 2022 15:31:01 +0200 Message-Id: <20220919133105.3129080-3-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In order to be able to send our Router Solicitations before the interface has a confirmed link-local address we have to be able to supply our own IPv6 header and skip source address selection and routing in the kernel. It is at least tricky to do with an AF_INET6 raw socket as some socket features, like SO_DONTROUTE/MSG_DONTROUTE, are not supported and others don't suppress the source address selection/checks. Additionally since we need to use the IPv6 unspecified address, which is represented as all 0 bytes, some syscalls interpret it as no value supplied. Build/parse the IPv6 header on our own and send/receive using an AF_PACKET socket. Once we have the link-local address, we switch to sending from that address. Add l_icmp6_client_set_link_local_address for the user to give it to us as they're likely to need it for DHCPv6. --- ell/dhcp6.c | 3 + ell/ell.sym | 1 + ell/icmp6.c | 324 ++++++++++++++++++++++++++++++------------------ ell/icmp6.h | 2 + ell/netconfig.c | 1 + 5 files changed, 213 insertions(+), 118 deletions(-) diff --git a/ell/dhcp6.c b/ell/dhcp6.c index 01d7eb3..15b451d 100644 --- a/ell/dhcp6.c +++ b/ell/dhcp6.c @@ -1619,6 +1619,9 @@ LIB_EXPORT bool l_dhcp6_client_set_link_local_address( if (inet_pton(AF_INET6, ll, &client->ll_address) != 1) return false; + if (!client->nora) + l_icmp6_client_set_link_local_address(client->icmp6, ll); + return true; } diff --git a/ell/ell.sym b/ell/ell.sym index ed66352..a28eb55 100644 --- a/ell/ell.sym +++ b/ell/ell.sym @@ -703,6 +703,7 @@ global: l_icmp6_client_set_nodelay; l_icmp6_client_set_rtnl; l_icmp6_client_set_route_priority; + l_icmp6_client_set_link_local_address; l_icmp6_router_get_address; l_icmp6_router_get_managed; l_icmp6_router_get_other; diff --git a/ell/icmp6.c b/ell/icmp6.c index c38df17..a2765ea 100644 --- a/ell/icmp6.c +++ b/ell/icmp6.c @@ -24,6 +24,7 @@ #include #endif +#define _GNU_SOURCE #include #include #include @@ -31,12 +32,16 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include +#include #include "private.h" #include "useful.h" @@ -75,135 +80,186 @@ { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \ { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } } +#define LLADDR_LINKLOCAL_ALLNODES_INIT \ + { 0x33,0x33,0,0,0,1 } +#define LLADDR_LINKLOCAL_ALLROUTERS_INIT \ + { 0x33,0x33,0,0,0,2 } -static const struct in6_addr in6addr_linklocal_allnodes_init = - IN6ADDR_LINKLOCAL_ALLNODES_INIT; - -static int add_mreq(int s, int ifindex, const struct in6_addr *mc_addr) -{ - struct ipv6_mreq mreq = { - .ipv6mr_interface = ifindex, - .ipv6mr_multiaddr = *mc_addr, - }; - - return setsockopt(s, IPPROTO_IPV6, - IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); -} - -static int icmp6_open_router_common(const struct icmp6_filter *filter, - int ifindex) +static int icmp6_open_router_solicitation(int ifindex) { int s; - int r; - int yes = 1; - int no = 0; - int nhops = 255; + struct sockaddr_ll addr; + struct sock_filter filter[] = { + /* A <- packet length */ + BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0), + /* A >= sizeof(nd_router_advert) ? */ + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, sizeof(struct ip6_hdr) + + sizeof(struct nd_router_advert), 1, 0), + /* ignore */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* A <- IP version + Traffic class */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 0), + /* A <- A & 0xf0 (Mask off version) */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0), + /* A == IPv6 ? */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6 << 4, 1, 0), + /* ignore */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* A <- Next Header */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, + offsetof(struct ip6_hdr, ip6_nxt)), + /* A == ICMPv6 ? */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 1, 0), + /* ignore */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* A <- ICMPv6 Type */ + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, sizeof(struct ip6_hdr) + + offsetof(struct icmp6_hdr, icmp6_type)), + /* A == Router Advertisement ? */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 1, 0), + /* ignore */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* A <- Payload Length */ + BPF_STMT(BPF_LD | BPF_H | BPF_ABS, + offsetof(struct ip6_hdr, ip6_plen)), + /* A >= sizeof(nd_router_advert) ? */ + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, + sizeof(struct nd_router_advert), 1, 0), + /* ignore */ + BPF_STMT(BPF_RET | BPF_K, 0), + /* return all */ + BPF_STMT(BPF_RET | BPF_K, 65535), + }; + const struct sock_fprog fprog = { + .len = L_ARRAY_SIZE(filter), + .filter = filter + }; + int one = 1; - s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); + s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_ICMPV6, - ICMP6_FILTER, filter, sizeof(struct icmp6_filter)); - if (r < 0) - goto fail; - - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no)); - if (r < 0) - goto fail; - - r = setsockopt(s, IPPROTO_IPV6, - IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); - if (r < 0) - goto fail; + if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, + &fprog, sizeof(fprog)) < 0) + goto error; - r = setsockopt(s, IPPROTO_IPV6, - IPV6_RECVHOPLIMIT, &yes, sizeof(yes)); - if (r < 0) - goto fail; + if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0) + goto error; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &nhops, sizeof(nhops)); - if (r < 0) - goto fail; + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_IPV6); + addr.sll_ifindex = ifindex; - r = setsockopt(s, SOL_SOCKET, SO_BINDTOIFINDEX, - &ifindex, sizeof(ifindex)); - if (r < 0 && errno == ENOPROTOOPT) { - struct ifreq ifr = { - .ifr_ifindex = ifindex, - }; - - r = ioctl(s, SIOCGIFNAME, &ifr); - if (r < 0) - goto fail; - - r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, - ifr.ifr_name, strlen(ifr.ifr_name) + 1); - } - - if (r < 0) - goto fail; - - r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &yes, sizeof(yes)); - if (r < 0) - goto fail; + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) + goto error; return s; -fail: - close(s); +error: + L_TFR(close(s)); return -errno; } -static int icmp6_open_router_solicitation(int ifindex) +static uint16_t icmp6_checksum(const struct iovec *iov, unsigned int iov_len) { - struct icmp6_filter filter; - int s; - int r; + const struct ip6_hdr *ip_hdr = iov[0].iov_base; + uint32_t sum = 0; + const uint16_t *ptr; + const uint16_t *buf_end; + /* Skip the real IPv6 header */ + unsigned int buf_offset = sizeof(struct ip6_hdr); - ICMP6_FILTER_SETBLOCKALL(&filter); - ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); + /* + * ICMPv6 checksum according to RFC 4443 Section 2.3, this includes + * the IPv6 payload + the IPv6 pseudo-header according to RFC 2460 + * Section 8.1, i.e. the two IPv6 addresses + the payload length + + * the header type. The caller must ensure that the IPv6 header is + * all in one buffer and that all buffer starts and lengths are + * 16-bit-aligned. + * + * We can skip all zero words such as the upper 16 bits of the + * payload length. No need to byteswap as the carry bits from + * either byte (high or low) accumulate in the other byte in + * exactly the same way. + */ + buf_end = (void *) &ip_hdr->ip6_src + 32; + for (ptr = (void *) &ip_hdr->ip6_src; ptr < buf_end; ) + sum += *ptr++; - s = icmp6_open_router_common(&filter, ifindex); - if (s < 0) - return s; + sum += ip_hdr->ip6_plen + htons(ip_hdr->ip6_nxt); - r = add_mreq(s, ifindex, &in6addr_linklocal_allnodes_init); - if (r < 0) { - close(s); - return -errno; + for (; iov_len; iov++, iov_len--) { + buf_end = iov->iov_base + iov->iov_len; + for (ptr = iov->iov_base + buf_offset; ptr < buf_end; ) + sum += *ptr++; + + buf_offset = 0; } - return s; + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; } -static int icmp6_send_router_solicitation(int s, const uint8_t mac[static 6]) +static int icmp6_send_router_solicitation(int s, int ifindex, + const uint8_t src_mac[static 6], + const struct in6_addr *src_ip) { - struct sockaddr_in6 dst = { - .sin6_family = AF_INET6, - .sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, - }; struct nd_router_solicit rs = { .nd_rs_type = ND_ROUTER_SOLICIT, + .nd_rs_code = 0, }; - struct nd_opt_hdr rs_opt = { + struct nd_opt_hdr rs_sllao = { .nd_opt_type = ND_OPT_SOURCE_LINKADDR, .nd_opt_len = 1, }; - struct iovec iov[3] = { + const size_t rs_sllao_size = sizeof(rs_sllao) + 6; + struct ip6_hdr ip_hdr = { + .ip6_flow = htonl(6 << 28), + .ip6_hops = 255, + .ip6_nxt = IPPROTO_ICMPV6, + .ip6_plen = htons(sizeof(rs) + rs_sllao_size), + .ip6_dst = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT, + }; + struct iovec iov[4] = { + { .iov_base = &ip_hdr, .iov_len = sizeof(ip_hdr) }, { .iov_base = &rs, .iov_len = sizeof(rs) }, - { .iov_base = &rs_opt, .iov_len = sizeof(rs_opt) }, - { .iov_base = (void *) mac, .iov_len = 6 } }; - + { .iov_base = &rs_sllao, .iov_len = sizeof(rs_sllao) }, + { .iov_base = (void *) src_mac, .iov_len = 6 } }; + + struct sockaddr_ll dst = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IPV6), + .sll_ifindex = ifindex, + .sll_addr = LLADDR_LINKLOCAL_ALLROUTERS_INIT, + .sll_halen = 6, + }; struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = iov, - .msg_iovlen = 3, + .msg_iovlen = L_ARRAY_SIZE(iov), }; int r; + memcpy(&ip_hdr.ip6_src, src_ip, 16); + + if (l_memeqzero(src_ip, 16)) { + /* + * radvd will discard and warn about RSs from the unspecified + * address with the SLLAO, omit that option by dropping the + * last two iov buffers. + */ + msg.msg_iovlen -= 2; + ip_hdr.ip6_plen = htons(ntohs(ip_hdr.ip6_plen) - rs_sllao_size); + } + + /* Don't byteswap the checksum */ + rs.nd_rs_cksum = icmp6_checksum(msg.msg_iov, msg.msg_iovlen); + r = sendmsg(s, &msg, 0); if (r < 0) return -errno; @@ -211,22 +267,23 @@ static int icmp6_send_router_solicitation(int s, const uint8_t mac[static 6]) return 0; } -static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src, - uint64_t *out_timestamp) +static int icmp6_receive(int s, void *buf, ssize_t *buf_len, + struct in6_addr *src, uint64_t *out_timestamp) { char c_msg_buf[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct timeval))]; - struct iovec iov = { - .iov_base = buf, - .iov_len = buf_len, + struct ip6_hdr ip_hdr; + struct iovec iov[2] = { + { .iov_base = &ip_hdr, .iov_len = sizeof(ip_hdr) }, + { .iov_base = buf, .iov_len = *buf_len - sizeof(ip_hdr) }, }; - struct sockaddr_in6 saddr; + struct sockaddr_ll saddr; struct msghdr msg = { .msg_name = (void *)&saddr, - .msg_namelen = sizeof(struct sockaddr_in6), + .msg_namelen = sizeof(struct sockaddr_ll), .msg_flags = 0, - .msg_iov = &iov, - .msg_iovlen = 1, + .msg_iov = iov, + .msg_iovlen = L_ARRAY_SIZE(iov), .msg_control = c_msg_buf, .msg_controllen = sizeof(c_msg_buf), }; @@ -238,22 +295,30 @@ static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src, if (l < 0) return -errno; - if ((size_t) l != buf_len) + if (l != *buf_len) return -EINVAL; - if (msg.msg_namelen != sizeof(struct sockaddr_in6) || - saddr.sin6_family != AF_INET6) - return -EPFNOSUPPORT; + if (ntohs(ip_hdr.ip6_plen) > iov[1].iov_len) + return -EMSGSIZE; + + iov[1].iov_len = ntohs(ip_hdr.ip6_plen); + + /* + * Unlikely but align length for icmp6_checksum(). We know we have + * at least sizeof(struct ip6_hdr) extra bytes in buf so we can + * append this 0 byte no problem. + */ + if (iov[1].iov_len & 1) + ((uint8_t *) buf)[iov[1].iov_len++] = 0x00; + + if (icmp6_checksum(iov, L_ARRAY_SIZE(iov))) + return -EBADMSG; + + if (ip_hdr.ip6_hops != 255) + return -EMULTIHOP; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level == SOL_IPV6 && - cmsg->cmsg_type == IPV6_HOPLIMIT && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - int hops = l_get_u32(CMSG_DATA(cmsg)); - - if (hops != 255) - return -EMULTIHOP; - } else if (cmsg->cmsg_level == SOL_SOCKET && + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) { @@ -263,7 +328,8 @@ static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src, } } - memcpy(src, &saddr.sin6_addr, sizeof(saddr.sin6_addr)); + *buf_len = ntohs(ip_hdr.ip6_plen); + memcpy(src, &ip_hdr.ip6_src, 16); *out_timestamp = timestamp ?: l_time_now(); return 0; } @@ -280,6 +346,7 @@ struct l_icmp6_client { struct l_timeout *timeout_send; uint64_t retransmit_time; struct l_io *io; + struct in6_addr src_ip; struct l_icmp6_router *ra; struct l_netlink *rtnl; @@ -432,12 +499,16 @@ static bool icmp6_client_read_handler(struct l_io *io, void *userdata) return false; } - ra = l_malloc(l); - if (icmp6_receive(s, ra, l, &src, ×tamp) < 0) - goto done; + if ((size_t) l < sizeof(struct ip6_hdr) + + sizeof(struct nd_router_advert)) { + CLIENT_DEBUG("Message too small - ignore"); + return true; + } - if ((size_t) l < sizeof(struct nd_router_advert)) { - CLIENT_DEBUG("Message to small - ignore"); + ra = l_malloc(l); + r = icmp6_receive(s, ra, &l, &src, ×tamp); + if (r < 0) { + CLIENT_DEBUG("icmp6_receive(): %s (%i)", strerror(-r), -r); goto done; } @@ -475,7 +546,8 @@ static void icmp6_client_timeout_send(struct l_timeout *timeout, SOLICITATION_INTERVAL); r = icmp6_send_router_solicitation(l_io_get_fd(client->io), - client->mac); + client->ifindex, client->mac, + &client->src_ip); if (r < 0) { CLIENT_DEBUG("Error sending Router Solicitation: %s", strerror(-r)); @@ -692,6 +764,22 @@ LIB_EXPORT bool l_icmp6_client_set_route_priority( return true; } +LIB_EXPORT bool l_icmp6_client_set_link_local_address( + struct l_icmp6_client *client, + const char *ll) +{ + if (unlikely(!client)) + return false; + + /* + * client->src_ip is all 0s initially which results in our Router + * Solicitations being sent from the IPv6 Unspecified Address, which + * is fine. Once we have a confirmed link-local address we use that + * as the source address. + */ + return inet_pton(AF_INET6, ll, &client->src_ip) == 1; +} + struct l_icmp6_router *_icmp6_router_new() { struct l_icmp6_router *r = l_new(struct l_icmp6_router, 1); diff --git a/ell/icmp6.h b/ell/icmp6.h index 615dba4..ffbb8a8 100644 --- a/ell/icmp6.h +++ b/ell/icmp6.h @@ -65,6 +65,8 @@ bool l_icmp6_client_set_rtnl(struct l_icmp6_client *client, struct l_netlink *rtnl); bool l_icmp6_client_set_route_priority(struct l_icmp6_client *client, uint32_t priority); +bool l_icmp6_client_set_link_local_address(struct l_icmp6_client *client, + const char *ll); char *l_icmp6_router_get_address(const struct l_icmp6_router *r); bool l_icmp6_router_get_managed(const struct l_icmp6_router *r); diff --git a/ell/netconfig.c b/ell/netconfig.c index bbe1dec..72c67a9 100644 --- a/ell/netconfig.c +++ b/ell/netconfig.c @@ -1532,6 +1532,7 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc, netconfig_addr_wait_unregister(nc, true); l_dhcp6_client_set_link_local_address(nc->dhcp6_client, ip); + l_icmp6_client_set_link_local_address(nc->icmp6_client, ip); /* * Only now that we have a link-local address start actual DHCPv6 From patchwork Mon Sep 19 13:31:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980450 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3CBE25C94 for ; Mon, 19 Sep 2022 13:31:14 +0000 (UTC) Received: by mail-wm1-f53.google.com with SMTP id o5so16815961wms.1 for ; Mon, 19 Sep 2022 06:31:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=bFHkYS/sEhT3zf09knf+Fmzjye5RGkHPkm/iEW7GAos=; b=sX7Ru8xy6tBBrxXsez4xuPt49R4QvA7wetMY542V1jjF14ZJNJXdu5o2jt6FIvQ9uX L6i1RUMlv66XOJckXpDuUMmHlA55YasFpRIfj4+BTN9d0y4IS5HUrdahEcFr8ifJwPPT HBSyWdS9INX3HC1K7JebsEPyJujcOvlohcKqPtjkazfFaZ5FalMT4397GCjAtSot28LZ VgFDau5KcsCUVPnnBC8s8wbpLAz6Of5NCnkDKedvwR+WOl0noccORgbzDSGg8xmQvxrQ Z2nB0VZJmoz+W+XVrpXGJtKOxauGAVrlGQyg1gNYOyFfaZZ/mU7bjGVvRr1Oidic1nz5 YYzw== X-Gm-Message-State: ACrzQf0Xm3ZSde5sLZkZpxgO8JWZFh8qIndDNw88AWNzE0KW90aNSgtj 9OU3mYpBfKWSRbSEx/lf36ts1nyAe7g33w== X-Google-Smtp-Source: AMsMyM6fQ61blsw9bDo/Ku4k7bKK2jTpQnoTEQ1sp/PuN+xJ2a3BJZHuF5cIomL9Jun7kPnxhzSOfw== X-Received: by 2002:a05:600c:5009:b0:3a9:f99e:7410 with SMTP id n9-20020a05600c500900b003a9f99e7410mr12593415wmr.5.1663594272269; Mon, 19 Sep 2022 06:31:12 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.11 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:11 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 4/7] rtnl: Add l_rtnl_address_get_in_addr Date: Mon, 19 Sep 2022 15:31:02 +0200 Message-Id: <20220919133105.3129080-4-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Similar to l_rtnl_route_get_dst_in_addr add a getter for the raw address value, avoiding extra string conversions. --- ell/ell.sym | 1 + ell/rtnl.c | 9 +++++++++ ell/rtnl.h | 1 + 3 files changed, 11 insertions(+) diff --git a/ell/ell.sym b/ell/ell.sym index a28eb55..d76b2ea 100644 --- a/ell/ell.sym +++ b/ell/ell.sym @@ -626,6 +626,7 @@ global: l_rtnl_address_clone; l_rtnl_address_free; l_rtnl_address_get_address; + l_rtnl_address_get_in_addr; l_rtnl_address_get_family; l_rtnl_address_get_prefix_length; l_rtnl_address_get_broadcast; diff --git a/ell/rtnl.c b/ell/rtnl.c index 2483e82..5909c4b 100644 --- a/ell/rtnl.c +++ b/ell/rtnl.c @@ -180,6 +180,15 @@ LIB_EXPORT bool l_rtnl_address_get_address(const struct l_rtnl_address *addr, out_buf); } +LIB_EXPORT const void *l_rtnl_address_get_in_addr( + const struct l_rtnl_address *addr) +{ + if (unlikely(!addr)) + return NULL; + + return addr->family == AF_INET ? (void *) &addr->in_addr : &addr->in6_addr; +} + LIB_EXPORT uint8_t l_rtnl_address_get_family(const struct l_rtnl_address *addr) { if (unlikely(!addr)) diff --git a/ell/rtnl.h b/ell/rtnl.h index ffd1d73..1e6b1fa 100644 --- a/ell/rtnl.h +++ b/ell/rtnl.h @@ -44,6 +44,7 @@ void l_rtnl_address_free(struct l_rtnl_address *addr); DEFINE_CLEANUP_FUNC(l_rtnl_address_free); bool l_rtnl_address_get_address(const struct l_rtnl_address *addr, char *out_buf); +const void *l_rtnl_address_get_in_addr(const struct l_rtnl_address *addr); uint8_t l_rtnl_address_get_family(const struct l_rtnl_address *addr); uint8_t l_rtnl_address_get_prefix_length(const struct l_rtnl_address *addr); bool l_rtnl_address_get_broadcast(const struct l_rtnl_address *addr, From patchwork Mon Sep 19 13:31:03 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980452 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 537B94C7C for ; Mon, 19 Sep 2022 13:31:15 +0000 (UTC) Received: by mail-wr1-f43.google.com with SMTP id g3so26640419wrq.13 for ; Mon, 19 Sep 2022 06:31:15 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=SF2WDjAspdgiG2bVwk0/aRVX3/lQbIhweVUga6gjKNs=; b=KrTmnmcdKfM5A3KKocX9OCS5JiVEfOSBDhvQveTtBAzUaRMrkYrzPagCBnNJoBAquf zVYfWCmHUpnfm9+f26VDzM2MYDid7wkPsXIXpltXdU3FQKi7VLffPqx82N+AuOZI6xd2 HqZyB0TeNDnZ0HDhufjRIIhnR4ec8OXt6RkHssLQ9vuz+JmPwEoKnP8fXNN5WAmUCpcq sE50+ZVVo91UhMuXvJ/ICggN7oewF9P7Ba6OHVLs9jf1sePLtrlQPfjFw6sdfZircb/b oTCdpsuxz6DkvGwsKnzwC7CluePb34sArryc58dEi7p6SHrdIl9eALrZLNcYDtopvW77 HVJQ== X-Gm-Message-State: ACrzQf2k0tUIVbT4l4VAB34ndBKLLhAKG/lT3XxTj2oL3kiw1QOReCRt ztCqoq49Nj/zRICaQQkPC7fWMoKhXW6iuA== X-Google-Smtp-Source: AMsMyM6zP62rzFfCz+zq+83U6Idk+Ni6WWaOdTk6p98EL8oafLy/g6zGPXoCVfWmKaXU5D2OGJvGbw== X-Received: by 2002:a5d:6d0e:0:b0:22a:7bb2:14ae with SMTP id e14-20020a5d6d0e000000b0022a7bb214aemr11376651wrq.379.1663594273216; Mon, 19 Sep 2022 06:31:13 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.12 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:12 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 5/7] netconfig: Decouple icmp6 start from dhcp6 start Date: Mon, 19 Sep 2022 15:31:03 +0200 Message-Id: <20220919133105.3129080-5-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In preparation for supporting generating SLAAC addresses, start the l_icmp6_client directly from the netconfig state machine. l_dhcp6_client still takes care of creating and destroying l_icmp6_client because it needs it for its own public API, but netconfig starts/stops/handles events locally. This also allows a slight optimization described in RFC4862 where if we're lucky, we will send the Router Solicitation simultaneously with the Neighbor Solicitation for the link-local addresses so as to avoid waiting the sum of the RA response time + DAD timeout and instead wait the longer of the two periods. --- ell/dhcp6.c | 3 ++ ell/netconfig.c | 102 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/ell/dhcp6.c b/ell/dhcp6.c index 15b451d..e234eb0 100644 --- a/ell/dhcp6.c +++ b/ell/dhcp6.c @@ -1473,6 +1473,9 @@ static void dhcp6_client_icmp6_event(struct l_icmp6_client *icmp6, { struct l_dhcp6_client *client = user_data; + if (client->nora) + return; + switch (event) { case L_ICMP6_CLIENT_EVENT_ROUTER_FOUND: { diff --git a/ell/netconfig.c b/ell/netconfig.c index 72c67a9..3ac0319 100644 --- a/ell/netconfig.c +++ b/ell/netconfig.c @@ -24,12 +24,13 @@ #include #endif +#include #include #include +#include #include #include #include -#include #include #include #include @@ -56,6 +57,7 @@ #include "net.h" #include "net-private.h" #include "acd.h" +#include "timeout.h" #include "netconfig.h" struct l_netconfig { @@ -87,6 +89,8 @@ struct l_netconfig { struct l_queue *icmp_route_data; struct l_acd *acd; unsigned int orig_disable_ipv6; + uint8_t mac[ETH_ALEN]; + struct l_timeout *ra_timeout; /* These objects, if not NULL, are owned by @addresses and @routes */ struct l_rtnl_address *v4_address; @@ -665,6 +669,35 @@ static void netconfig_dhcp6_event_handler(struct l_dhcp6_client *client, } } +static bool netconfig_match(const void *a, const void *b) +{ + return a == b; +} + +static bool netconfig_check_start_dhcp6(struct l_netconfig *nc) +{ + /* Don't start DHCPv6 until we get an RA with the managed bit set */ + if (nc->ra_timeout) + return true; + + /* Don't start DHCPv6 while waiting for the link-local address */ + if (l_queue_find(addr_wait_list, netconfig_match, nc)) + return true; + + return l_dhcp6_client_start(nc->dhcp6_client); +} + +static void netconfig_ra_timeout_cb(struct l_timeout *timeout, void *user_data) +{ + struct l_netconfig *nc = user_data; + + l_timeout_remove(l_steal_ptr(nc->ra_timeout)); + + /* No Router Advertisements received, assume no DHCPv6 or SLAAC */ + l_icmp6_client_stop(nc->icmp6_client); + netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED); +} + static uint64_t now; static bool netconfig_check_route_expired(void *data, void *user_data) @@ -883,23 +916,23 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, const struct l_icmp6_router *r; struct netconfig_route_data *default_rd; unsigned int i; + bool first_ra = false; if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND) return; r = event_data; - /* - * Note: If this is the first RA received, the l_dhcp6_client - * will have received the event before us and will be acting - * on it by now. - */ - - if (nc->v6_gateway_override) - return; + if (nc->ra_timeout) { + first_ra = true; + l_timeout_remove(l_steal_ptr(nc->ra_timeout)); + } netconfig_expire_routes(nc); + if (nc->v6_gateway_override) + goto process_nondefault_routes; + /* Process the default gateway information */ default_rd = netconfig_find_icmp6_route(nc, r->address, NULL); @@ -922,6 +955,7 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, else if (default_rd && !r->lifetime) netconfig_remove_icmp6_route(nc, default_rd); +process_nondefault_routes: /* * Process the onlink and offlink routes, from the Router * Advertisement's Prefix Information options and Route @@ -950,6 +984,16 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, netconfig_remove_icmp6_route(nc, rd); } + /* See if we should start DHCPv6 now */ + if (first_ra) { + if (!l_icmp6_router_get_managed(r) || + !netconfig_check_start_dhcp6(nc)) { + netconfig_emit_event(nc, AF_INET6, + L_NETCONFIG_EVENT_FAILED); + return; + } + } + /* * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE. * We should probably instead save the affected routes in separate @@ -1046,6 +1090,7 @@ LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex) nc, NULL); nc->dhcp6_client = l_dhcp6_client_new(ifindex); + l_dhcp6_client_set_nora(nc->dhcp6_client, true); l_dhcp6_client_set_event_handler(nc->dhcp6_client, netconfig_dhcp6_event_handler, nc, NULL); @@ -1535,10 +1580,10 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc, l_icmp6_client_set_link_local_address(nc->icmp6_client, ip); /* - * Only now that we have a link-local address start actual DHCPv6 - * setup. + * Only now that we have a link-local address see if we can start + * actual DHCPv6 setup. */ - if (l_dhcp6_client_start(nc->dhcp6_client)) + if (netconfig_check_start_dhcp6(nc)) return; netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED); @@ -1687,6 +1732,35 @@ configure_ipv6: l_queue_push_tail(addr_wait_list, netconfig); + if (!l_net_get_mac_address(netconfig->ifindex, netconfig->mac)) + goto unregister; + + l_dhcp6_client_set_address(netconfig->dhcp6_client, ARPHRD_ETHER, + netconfig->mac, ETH_ALEN); + l_icmp6_client_set_address(netconfig->icmp6_client, netconfig->mac); + + /* + * RFC4862 Section 4: "To speed the autoconfiguration process, a host + * may generate its link-local address (and verify its uniqueness) in + * parallel with waiting for a Router Advertisement. Because a router + * may delay responding to a Router Solicitation for a few seconds, + * the total time needed to complete autoconfiguration can be + * significantly longer if the two steps are done serially." + * + * We don't know whether we have the LL address yet. The interface + * may have been just brought up and DAD may still running or the LL + * address may have been deleted and won't be added until + * netconfig_ifaddr_ipv6_dump_done_cb() writes the /proc settings. + * In any case the Router Solicitation doesn't depend on having the + * LL address so send it now. We won't start DHCPv6 however until we + * have both the LL address and the Router Advertisement. + */ + if (!l_icmp6_client_start(netconfig->icmp6_client)) + goto unregister; + + netconfig->ra_timeout = l_timeout_create(10, netconfig_ra_timeout_cb, + netconfig, NULL); + done: netconfig->started = true; return true; @@ -1717,6 +1791,9 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig) if (netconfig->signal_expired_work) l_idle_remove(l_steal_ptr(netconfig->signal_expired_work)); + if (netconfig->ra_timeout) + l_timeout_remove(l_steal_ptr(netconfig->ra_timeout)); + netconfig_addr_wait_unregister(netconfig, false); netconfig_update_cleanup(netconfig); @@ -1734,6 +1811,7 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig) l_dhcp_client_stop(netconfig->dhcp_client); l_dhcp6_client_stop(netconfig->dhcp6_client); + l_icmp6_client_stop(netconfig->icmp6_client); l_acd_destroy(l_steal_ptr(netconfig->acd)); From patchwork Mon Sep 19 13:31:04 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980453 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 446564C79 for ; Mon, 19 Sep 2022 13:31:16 +0000 (UTC) Received: by mail-wr1-f49.google.com with SMTP id c11so47521286wrp.11 for ; Mon, 19 Sep 2022 06:31:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=4Z2wckLxQC+Mie9Xxz0mnDuGVXpXoSbzpsP1jMWy8VE=; b=CKzURSNKYsBW79XxbqRBA4sbofGzUREXWst9XCc0m9Z3/HUoLvXwWhAsbFxiukWPZH OgS67mOWdXl5JThiKkZFmO3xY/yW1v6oW9OAuh4Zb54NyIQl0I6hdu36qH7tIsSVLQLd GO7RC98lvs7+LTXDPTtq7kCe6Dkd/UxnFrKPVPcSX0cOx5DWsBzjghWLcMUpMQBNhDLY UIe3IN58ZYbLlY1+fFFAqy+R4x+GnxHZLJbSrQKCNFh5PeMSpHGbjQmumfobhiATZI7f 6QI1XBO+PVpnioSF0GdgVoyp1Nvmc4E7Boinx6CWBumYUCxj8Lw3e2SFDYhVxHGkPKxv 9i/g== X-Gm-Message-State: ACrzQf087mGCjgB8EF6vENvUuAJnumJk4Jcujf/Nr/q3O9dFrPejttLa WGDmiL64YO0afPPBTvDL+np5HK/4gPJL4g== X-Google-Smtp-Source: AMsMyM5uJrsoZcgg2DQyHWUMyQ65NgKAQVzT7kMM1MDzWFZk1kQYYgkBn1S+0g/DjZ8V/P5A42Eh5w== X-Received: by 2002:a05:6000:1c1c:b0:228:de40:986f with SMTP id ba28-20020a0560001c1c00b00228de40986fmr11144201wrb.212.1663594274103; Mon, 19 Sep 2022 06:31:14 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.13 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:13 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 6/7] netconfig: Create SLAAC address Date: Mon, 19 Sep 2022 15:31:04 +0200 Message-Id: <20220919133105.3129080-6-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 If the Router Advertisement doesn't indicate DHCPv6 is available and includes prefixes for address auto-configuration, fall back to using those to generate a single IPv6 address. Other settings such as DNS are not supported in this mode yet and there's no renewal timer for when the address lifetime is finite. --- ell/netconfig.c | 184 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 174 insertions(+), 10 deletions(-) diff --git a/ell/netconfig.c b/ell/netconfig.c index 3ac0319..3336b30 100644 --- a/ell/netconfig.c +++ b/ell/netconfig.c @@ -91,6 +91,11 @@ struct l_netconfig { unsigned int orig_disable_ipv6; uint8_t mac[ETH_ALEN]; struct l_timeout *ra_timeout; + enum { + NETCONFIG_V6_METHOD_UNSET, + NETCONFIG_V6_METHOD_DHCP, + NETCONFIG_V6_METHOD_SLAAC, + } v6_auto_method; /* These objects, if not NULL, are owned by @addresses and @routes */ struct l_rtnl_address *v4_address; @@ -677,7 +682,7 @@ static bool netconfig_match(const void *a, const void *b) static bool netconfig_check_start_dhcp6(struct l_netconfig *nc) { /* Don't start DHCPv6 until we get an RA with the managed bit set */ - if (nc->ra_timeout) + if (nc->ra_timeout || nc->v6_auto_method != NETCONFIG_V6_METHOD_DHCP) return true; /* Don't start DHCPv6 while waiting for the link-local address */ @@ -698,6 +703,109 @@ static void netconfig_ra_timeout_cb(struct l_timeout *timeout, void *user_data) netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED); } +static void netconfig_add_slaac_address(struct l_netconfig *nc, + const struct l_icmp6_router *r) +{ + unsigned int i; + const struct autoconf_prefix_info *longest = &r->ac_prefixes[0]; + uint8_t addr[16]; + char addr_str[INET6_ADDRSTRLEN]; + uint32_t p, v; + + /* Find the autoconfiguration prefix that offers the longest lifetime */ + for (i = 1; i < r->n_ac_prefixes; i++) + if (r->ac_prefixes[i].preferred_lifetime > + longest->preferred_lifetime) + longest = &r->ac_prefixes[i]; + + memcpy(addr, longest->prefix, 8); + /* EUI-64-based Interface Identifier (RFC2464 Section 4) */ + addr[ 8] = nc->mac[0] ^ 0x02; + addr[ 9] = nc->mac[1]; + addr[10] = nc->mac[2]; + addr[11] = 0xff; + addr[12] = 0xfe; + addr[13] = nc->mac[3]; + addr[14] = nc->mac[4]; + addr[15] = nc->mac[5]; + inet_ntop(AF_INET6, addr, addr_str, sizeof(addr_str)); + p = longest->preferred_lifetime; + v = longest->valid_lifetime; + + nc->v6_address = l_rtnl_address_new(addr_str, 128); + l_rtnl_address_set_noprefixroute(nc->v6_address, true); + + if (p != 0xffffffff || v != 0xffffffff) { + l_rtnl_address_set_lifetimes(nc->v6_address, + p != 0xffffffff ? p : 0, + v != 0xffffffff ? v : 0); + l_rtnl_address_set_expiry(nc->v6_address, + p != 0xffffffff ? + r->start_time + p * L_USEC_PER_SEC : 0, + v != 0xffffffff ? + r->start_time + v * L_USEC_PER_SEC : 0); + } + + l_queue_push_tail(nc->addresses.current, nc->v6_address); + l_queue_push_tail(nc->addresses.added, nc->v6_address); + netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_CONFIGURE); + + /* TODO: set a renew timeout */ +} + +static void netconfig_set_slaac_address_lifetimes(struct l_netconfig *nc, + const struct l_icmp6_router *r) +{ + const uint8_t *addr = l_rtnl_address_get_in_addr(nc->v6_address); + bool updated = false; + uint64_t p_expiry; + uint64_t v_expiry; + uint32_t remaining = 0xffffffff; + unsigned int i; + + if (L_WARN_ON(!addr)) + return; + + l_rtnl_address_get_expiry(nc->v6_address, &p_expiry, &v_expiry); + + if (v_expiry) + remaining = (v_expiry - r->start_time) / L_USEC_PER_SEC; + + for (i = 0; i < r->n_ac_prefixes; i++) { + const struct autoconf_prefix_info *prefix = &r->ac_prefixes[i]; + uint32_t p = prefix->preferred_lifetime; + uint32_t v = prefix->valid_lifetime; + + if (memcmp(prefix->prefix, addr, 8)) + continue; + + /* RFC4862 Section 5.5.3 e) */ + if (v < 120 * 60 && v < remaining) + v = 120 * 60; /* 2 hours */ + + l_rtnl_address_set_lifetimes(nc->v6_address, + p != 0xffffffff ? p : 0, + v != 0xffffffff ? v : 0); + p_expiry = p != 0xffffffff ? r->start_time + p * L_USEC_PER_SEC : 0; + v_expiry = v != 0xffffffff ? r->start_time + v * L_USEC_PER_SEC : 0; + l_rtnl_address_set_expiry(nc->v6_address, p_expiry, v_expiry); + updated = true; + + /* + * TODO: modify the renew timeout. + * + * Also we probably want to apply a mechanism similar to that + * in netconfig_check_route_need_update() to avoid generating + * and UPDATED event for every RA that covers this prefix + * with constant lifetime values. + */ + } + + if (updated && !l_queue_find(nc->addresses.added, netconfig_match, + nc->v6_address)) + l_queue_push_tail(nc->addresses.updated, nc->v6_address); +} + static uint64_t now; static bool netconfig_check_route_expired(void *data, void *user_data) @@ -916,17 +1024,14 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client, const struct l_icmp6_router *r; struct netconfig_route_data *default_rd; unsigned int i; - bool first_ra = false; if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND) return; r = event_data; - if (nc->ra_timeout) { - first_ra = true; + if (nc->ra_timeout) l_timeout_remove(l_steal_ptr(nc->ra_timeout)); - } netconfig_expire_routes(nc); @@ -984,16 +1089,72 @@ process_nondefault_routes: netconfig_remove_icmp6_route(nc, rd); } - /* See if we should start DHCPv6 now */ - if (first_ra) { - if (!l_icmp6_router_get_managed(r) || - !netconfig_check_start_dhcp6(nc)) { + /* + * For lack of a better policy, select between DHCPv6 and SLAAC based + * on the first RA received. Prefer DHCPv6. + * + * Just like we currently only request one address in l_dhcp6_client, + * we only set up one address using SLAAC regardless of how many + * prefixes are available. Generate the address in the prefix that + * offers the longest preferred_lifetime. + */ + if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET && + l_icmp6_router_get_managed(r)) { + nc->v6_auto_method = NETCONFIG_V6_METHOD_DHCP; + + if (!netconfig_check_start_dhcp6(nc)) { netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED); return; } + + goto emit_event; + } + + /* + * DHCP not available according to this router, check if any of the + * prefixes allow SLAAC. + */ + if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET && + r->n_ac_prefixes) { + nc->v6_auto_method = NETCONFIG_V6_METHOD_SLAAC; + + /* + * The DAD for the link-local address may be still running + * but again we can generate the global address already and + * commit it to start in-kernel DAD for it. + * + * The global address alone should work for most uses. On + * the other hand since both the link-local address and the + * global address are based on the same MAC, there's some + * correlation between one failing DAD and the other + * failing DAD due to another host using the same address. + * As RFC4862 Section 5.4 notes we can't rely on that to + * skip DAD for one of the addresses. + */ + + netconfig_add_slaac_address(nc, r); + return; } + /* Neither method seems available, fail */ + if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET) { + netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED); + return; + } + + /* DHCP already started or waiting for the LL address, nothing to do */ + if (nc->v6_auto_method == NETCONFIG_V6_METHOD_DHCP) + goto emit_event; + + /* + * Otherwise we already have a SLAAC address, just check if any of the + * auto-configuration prefixes in this RA covers our existing address + * and allows us to extend its lifetime. + */ + netconfig_set_slaac_address_lifetimes(nc, r); + +emit_event: /* * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE. * We should probably instead save the affected routes in separate @@ -1003,7 +1164,8 @@ process_nondefault_routes: if (!l_queue_isempty(nc->routes.added) || !l_queue_isempty(nc->routes.updated) || !l_queue_isempty(nc->routes.removed) || - !l_queue_isempty(nc->routes.expired)) + !l_queue_isempty(nc->routes.expired) || + !l_queue_isempty(nc->addresses.updated)) netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_UPDATE); } @@ -1706,6 +1868,8 @@ configure_ipv6: goto done; } + netconfig->v6_auto_method = NETCONFIG_V6_METHOD_UNSET; + /* * We only care about being on addr_wait_list if we're waiting for * the link-local address for DHCP6. Add ourself to the list here From patchwork Mon Sep 19 13:31:05 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Zaborowski X-Patchwork-Id: 12980454 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 176B06101 for ; Mon, 19 Sep 2022 13:31:16 +0000 (UTC) Received: by mail-wr1-f46.google.com with SMTP id g3so26640559wrq.13 for ; Mon, 19 Sep 2022 06:31:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date; bh=AmjmGQI413kSQYZ8UfQUov1MoU7e7RMicUDCcchBUaQ=; b=ublpQKjlDj8YJwmknJhwn1aWz0wpPv9VuMecgrWCx6Gw1jhOtJ95DYqdeNjvHZIlnI y2Nu3/btas257qw1yCpCmoXnPfg53zRsgN98BWWBeaVLOgesxM4sDXhs/HPiJT+v15Ms JVVDiQbNLIB/Z+CtxGTUGmNdihuQuVWeEJgfY6GBZgFLCkVD7rORnT6VIHYWd4h4fgTx p1mz7eR6MOMUmAsfyzitRMkTqNu4YYynBBvuDZmGypWbfPn3UoIV9W9uHlOow6LXHj0/ gvYmy/9ThjFXMNctp8jm5IQ1PfouV1SoxdzIOKcizjBD1YsNXsPQ1/dZVIhBF4Q5TObS kpAQ== X-Gm-Message-State: ACrzQf0xiuSLR9FBjuRiFBmhIktv8o/Hxf52OuVVmHl6OdSEkW7PbFHN Xi1JQ/PwiWdWh6xry7vrdPdww+y3DofccQ== X-Google-Smtp-Source: AMsMyM7U/67yaGGvFNh/+tmESdP5VGDT7jO4RMAeOkFN9nXYLi+lEbV+dp3ImMsSMG9BeH+1YNb0fA== X-Received: by 2002:adf:dd83:0:b0:22a:eb0a:7fa7 with SMTP id x3-20020adfdd83000000b0022aeb0a7fa7mr7059408wrl.135.1663594275019; Mon, 19 Sep 2022 06:31:15 -0700 (PDT) Received: from iss.ger.corp.intel.com ([82.213.228.103]) by smtp.gmail.com with ESMTPSA id az24-20020adfe198000000b00228d7078c4esm14252463wrb.4.2022.09.19.06.31.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Sep 2022 06:31:14 -0700 (PDT) From: Andrew Zaborowski To: ell@lists.linux.dev Subject: [PATCH 7/7] netconfig: Control optimistic DAD Date: Mon, 19 Sep 2022 15:31:05 +0200 Message-Id: <20220919133105.3129080-7-andrew.zaborowski@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220919133105.3129080-1-andrew.zaborowski@intel.com> References: <20220919133105.3129080-1-andrew.zaborowski@intel.com> Precedence: bulk X-Mailing-List: ell@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Enable or disable optimistic DAD for the interface using /proc. Add l_netconfig_set_optimistic_dad_enabled() for the user to request that RFC 4429 optimistic DAD be enabled. As recommended in the RFC, we'll only actually enable optimistic DAD for the automatic address generation methods and disable it if a static IPv6 address is configured. The default if l_netconfig_set_optimistic_dad_enabled() isn't used is to disable optimistic DAD always. Optimistic DAD can shorten practical IPv6 setup time by an amount on the order of a second by allowing the link-local address and the global address to be used immediately after being generated. For the link-local address this means that DHCPv6 may start sooner and for the global address, which is added to the kernel by the L_NETCONFIG_EVENT_CONFIGURE event handler, the user can start establishing connections sooner. With DHCPv6 and some luck the two savings may compound, with SLAAC only the global address's DAD time should matter. --- ell/ell.sym | 1 + ell/netconfig.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- ell/netconfig.h | 2 ++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ell/ell.sym b/ell/ell.sym index d76b2ea..6df9024 100644 --- a/ell/ell.sym +++ b/ell/ell.sym @@ -751,6 +751,7 @@ global: l_netconfig_set_dns_override; l_netconfig_set_domain_names_override; l_netconfig_set_acd_enabled; + l_netconfig_set_optimistic_dad_enabled; l_netconfig_check_config; l_netconfig_reset_config; l_netconfig_start; diff --git a/ell/netconfig.c b/ell/netconfig.c index 3336b30..7a9ee89 100644 --- a/ell/netconfig.c +++ b/ell/netconfig.c @@ -76,6 +76,7 @@ struct l_netconfig { char *v6_gateway_override; char **v6_dns_override; char **v6_domain_names_override; + bool optimistic_dad_enabled; bool started; struct l_idle *do_static_work; @@ -89,6 +90,7 @@ struct l_netconfig { struct l_queue *icmp_route_data; struct l_acd *acd; unsigned int orig_disable_ipv6; + unsigned int orig_optimistic_dad; uint8_t mac[ETH_ALEN]; struct l_timeout *ra_timeout; enum { @@ -1490,6 +1492,17 @@ LIB_EXPORT bool l_netconfig_set_acd_enabled(struct l_netconfig *netconfig, return true; } +LIB_EXPORT bool l_netconfig_set_optimistic_dad_enabled( + struct l_netconfig *netconfig, + bool enabled) +{ + if (unlikely(!netconfig || netconfig->started)) + return false; + + netconfig->optimistic_dad_enabled = enabled; + return true; +} + static bool netconfig_check_family_config(struct l_netconfig *nc, uint8_t family) { @@ -1724,7 +1737,8 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc, struct in6_addr in6; _auto_(l_free) char *ip = NULL; - if (ifa->ifa_flags & IFA_F_TENTATIVE) + if ((ifa->ifa_flags & IFA_F_TENTATIVE) && + !(ifa->ifa_flags & IFA_F_OPTIMISTIC)) return; if (!nc->started) @@ -1828,6 +1842,8 @@ static void netconfig_ifaddr_ipv6_dump_done_cb(void *user_data) LIB_EXPORT bool l_netconfig_start(struct l_netconfig *netconfig) { + bool optimistic_dad; + if (unlikely(!netconfig || netconfig->started)) return false; @@ -1855,6 +1871,28 @@ configure_ipv6: if (!netconfig->v6_enabled) goto done; + /* + * Enable optimistic DAD if the user has requested it *and* it is + * recommended by RFC 4429 Section 3.1 for the address generation + * method in use: + * * mac-based Interface ID such as EUI-64 + * * random + * * well-distributed hash function + * * DHCPv6 + * i.e. all autoconfiguration methods. In any other case disable + * it. + */ + optimistic_dad = netconfig->optimistic_dad_enabled && + !netconfig->v6_static_addr; + netconfig->orig_optimistic_dad = + netconfig_proc_read_ipv6_uint_setting(netconfig, + "optimistic_dad"); + + if (!!netconfig->orig_optimistic_dad != optimistic_dad) + netconfig_proc_write_ipv6_uint_setting(netconfig, + "optimistic_dad", + optimistic_dad ? 1 : 0); + if (netconfig->v6_static_addr) { /* * We're basically ready to configure the interface @@ -1944,6 +1982,8 @@ unregister: LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig) { + bool optimistic_dad; + if (unlikely(!netconfig || !netconfig->started)) return; @@ -1985,6 +2025,13 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig) netconfig->orig_disable_ipv6); netconfig->orig_disable_ipv6 = 0; } + + optimistic_dad = netconfig->optimistic_dad_enabled && + !netconfig->v6_static_addr; + if (!!netconfig->orig_optimistic_dad != optimistic_dad) + netconfig_proc_write_ipv6_uint_setting(netconfig, + "optimistic_dad", + netconfig->orig_optimistic_dad); } /* diff --git a/ell/netconfig.h b/ell/netconfig.h index fb3c536..3c024fe 100644 --- a/ell/netconfig.h +++ b/ell/netconfig.h @@ -69,6 +69,8 @@ bool l_netconfig_set_dns_override(struct l_netconfig *netconfig, uint8_t family, bool l_netconfig_set_domain_names_override(struct l_netconfig *netconfig, uint8_t family, char **names); bool l_netconfig_set_acd_enabled(struct l_netconfig *netconfig, bool enabled); +bool l_netconfig_set_optimistic_dad_enabled(struct l_netconfig *netconfig, + bool enabled); bool l_netconfig_check_config(struct l_netconfig *netconfig); bool l_netconfig_reset_config(struct l_netconfig *netconfig);