mirror of
https://github.com/systemd/systemd
synced 2026-04-06 23:24:52 +02:00
Compare commits
13 Commits
e44a47d186
...
095eaf7130
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
095eaf7130 | ||
|
|
26591ffffd | ||
|
|
51f6c93d21 | ||
|
|
bae62bee43 | ||
|
|
6f593f7226 | ||
|
|
510afa460a | ||
|
|
b3a1fb795a | ||
|
|
b07d8145e1 | ||
|
|
18b23bd493 | ||
|
|
344b3cff36 | ||
|
|
7a2e124b08 | ||
|
|
f2a3a133ec | ||
|
|
c463ae74ae |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -1,3 +1,5 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
# vi: ts=2 sw=2 et:
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
#
|
||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -7,6 +11,9 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '0 1 * * *'
|
- cron: '0 1 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@ -16,7 +23,6 @@ jobs:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
3
.github/workflows/labeler.yml
vendored
3
.github/workflows/labeler.yml
vendored
@ -9,11 +9,12 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@69da01b8e0929f147b8943611bee75ee4175a49e
|
- uses: actions/labeler@69da01b8e0929f147b8943611bee75ee4175a49e
|
||||||
with:
|
with:
|
||||||
|
|||||||
@ -274,6 +274,8 @@ mouse:usb:v04b3p3107:name:*
|
|||||||
|
|
||||||
# Kensington Expert Mouse trackball
|
# Kensington Expert Mouse trackball
|
||||||
mouse:usb:v047dp1020:*Kensington Expert Mouse*:*
|
mouse:usb:v047dp1020:*Kensington Expert Mouse*:*
|
||||||
|
mouse:usb:v047dp8018:name:Kensington Expert Wireless TB Mouse:*
|
||||||
|
mouse:bluetooth:v047dp8019:name:Expert Wireless TB Mouse:*
|
||||||
ID_INPUT_TRACKBALL=1
|
ID_INPUT_TRACKBALL=1
|
||||||
MOUSE_DPI=400@125
|
MOUSE_DPI=400@125
|
||||||
|
|
||||||
|
|||||||
@ -2193,41 +2193,75 @@ Table=1234</programlisting></para>
|
|||||||
<term><varname>Token=</varname></term>
|
<term><varname>Token=</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>Specifies an optional address generation mode for the Stateless Address
|
<para>Specifies an optional address generation mode for the Stateless Address
|
||||||
Autoconfiguration (SLAAC). Supported modes are <literal>eui64</literal>,
|
Autoconfiguration (SLAAC). The following values are supported:</para>
|
||||||
<literal>static</literal>, and <literal>prefixstable</literal>.</para>
|
|
||||||
|
|
||||||
<para>When the mode is set to <literal>eui64</literal>, then the EUI-64 algorithm will be
|
<variablelist>
|
||||||
used to generate an address for that prefix.</para>
|
<varlistentry>
|
||||||
|
<term><option>eui64</option></term>
|
||||||
<para>When the mode is set to <literal>static</literal>, an IPv6 address must be
|
<listitem>
|
||||||
specified after a colon (<literal>:</literal>), and the lower bits of the supplied
|
<para>
|
||||||
address are combined with the upper bits of a prefix received in a Router Advertisement
|
The EUI-64 algorithm will be used to generate an address for that prefix.
|
||||||
(RA) message to form a complete address. Note that if multiple prefixes are received in an
|
</para>
|
||||||
RA message, or in multiple RA messages, addresses will be formed from each of them using
|
</listitem>
|
||||||
the supplied address. This mode implements SLAAC but uses a static interface identifier
|
</varlistentry>
|
||||||
instead of an identifier generated by using the EUI-64 algorithm. Because the interface
|
<varlistentry>
|
||||||
identifier is static, if Duplicate Address Detection detects that the computed address is a
|
<term><option>static:<replaceable>ADDRESS</replaceable></option></term>
|
||||||
duplicate (in use by another node on the link), then this mode will fail to provide an
|
<listitem>
|
||||||
address for that prefix. If an IPv6 address without mode is specified, then
|
<para>
|
||||||
<literal>static</literal> mode is assumed.</para>
|
An IPv6 address must be specified after a colon (<literal>:</literal>), and the
|
||||||
|
lower bits of the supplied address are combined with the upper bits of a prefix
|
||||||
<para>When the mode is set to <literal>prefixstable</literal> the
|
received in a Router Advertisement (RA) message to form a complete address. Note
|
||||||
<ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink> algorithm for generating
|
that if multiple prefixes are received in an RA message, or in multiple RA messages,
|
||||||
interface identifiers will be used. This mode can optionally take an IPv6 address separated
|
addresses will be formed from each of them using the supplied address. This mode
|
||||||
with a colon (<literal>:</literal>). If an IPv6 address is specified, then an interface
|
implements SLAAC but uses a static interface identifier instead of an identifier
|
||||||
identifier is generated only when a prefix received in an RA message matches the supplied
|
generated by using the EUI-64 algorithm. Because the interface identifier is static,
|
||||||
address.</para>
|
if Duplicate Address Detection detects that the computed address is a duplicate
|
||||||
|
(in use by another node on the link), then this mode will fail to provide an address
|
||||||
|
for that prefix. If an IPv6 address without mode is specified, then
|
||||||
|
<literal>static</literal> mode is assumed.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>prefixstable[:<replaceable>ADDRESS</replaceable>][,<replaceable>UUID</replaceable>]</option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The algorithm specified in
|
||||||
|
<ulink url="https://tools.ietf.org/html/rfc7217">RFC 7217</ulink> will be used to
|
||||||
|
generate interface identifiers. This mode can optionally take an IPv6 address
|
||||||
|
separated with a colon (<literal>:</literal>). If an IPv6 address is specified,
|
||||||
|
then an interface identifier is generated only when a prefix received in an RA
|
||||||
|
message matches the supplied address.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This mode can also optionally take a non-null UUID in the format which
|
||||||
|
<function>sd_id128_from_string()</function> accepts, e.g.
|
||||||
|
<literal>86b123b969ba4b7eb8b3d8605123525a</literal> or
|
||||||
|
<literal>86b123b9-69ba-4b7e-b8b3-d8605123525a</literal>. If a UUID is specified, the
|
||||||
|
value is used as the secret key to generate interface identifiers. If not specified,
|
||||||
|
then an application specific ID generated with the system's machine-ID will be used
|
||||||
|
as the secret key. See
|
||||||
|
<citerefentry><refentrytitle>sd-id128</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
<citerefentry><refentrytitle>sd_id128_from_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
and
|
||||||
|
<citerefentry><refentrytitle>sd_id128_get_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Note that the <literal>prefixstable</literal> algorithm uses both the interface
|
||||||
|
name and MAC address as input to the hash to compute the interface identifier, so
|
||||||
|
if either of those are changed the resulting interface identifier (and address)
|
||||||
|
will be changed, even if the prefix received in the RA message has not been
|
||||||
|
changed.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
<para>If no address generation mode is specified (which is the default), or a received
|
<para>If no address generation mode is specified (which is the default), or a received
|
||||||
prefix does not match any of the addresses provided in <literal>prefixstable</literal>
|
prefix does not match any of the addresses provided in <literal>prefixstable</literal>
|
||||||
mode, then the EUI-64 algorithm will be used to form an interface identifier for that
|
mode, then the EUI-64 algorithm will be used to form an interface identifier for that
|
||||||
prefix.</para>
|
prefix.</para>
|
||||||
|
|
||||||
<para>Note that the <literal>prefixstable</literal> algorithm uses both the interface
|
|
||||||
name and MAC address as input to the hash to compute the interface identifier, so if either
|
|
||||||
of those are changed the resulting interface identifier (and address) will be changed, even
|
|
||||||
if the prefix received in the RA message has not been changed.</para>
|
|
||||||
|
|
||||||
<para>This setting can be specified multiple times. If an empty string is assigned, then
|
<para>This setting can be specified multiple times. If an empty string is assigned, then
|
||||||
the all previous assignments are cleared.</para>
|
the all previous assignments are cleared.</para>
|
||||||
|
|
||||||
|
|||||||
@ -115,6 +115,8 @@ sources = files('''
|
|||||||
networkd-nexthop.h
|
networkd-nexthop.h
|
||||||
networkd-queue.c
|
networkd-queue.c
|
||||||
networkd-queue.h
|
networkd-queue.h
|
||||||
|
networkd-route-util.c
|
||||||
|
networkd-route-util.h
|
||||||
networkd-route.c
|
networkd-route.c
|
||||||
networkd-route.h
|
networkd-route.h
|
||||||
networkd-routing-policy-rule.c
|
networkd-routing-policy-rule.c
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "sd-id128.h"
|
#include "sd-id128.h"
|
||||||
|
|
||||||
|
#include "id128-util.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
#include "networkd-address-generation.h"
|
#include "networkd-address-generation.h"
|
||||||
#include "networkd-link.h"
|
#include "networkd-link.h"
|
||||||
@ -35,6 +36,7 @@ typedef enum AddressGenerationType {
|
|||||||
typedef struct IPv6Token {
|
typedef struct IPv6Token {
|
||||||
AddressGenerationType type;
|
AddressGenerationType type;
|
||||||
struct in6_addr address;
|
struct in6_addr address;
|
||||||
|
sd_id128_t secret_key;
|
||||||
} IPv6Token;
|
} IPv6Token;
|
||||||
|
|
||||||
static void generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
|
static void generate_eui64_address(const Link *link, const struct in6_addr *prefix, struct in6_addr *ret) {
|
||||||
@ -117,29 +119,35 @@ static void generate_stable_private_address_one(
|
|||||||
static int generate_stable_private_address(
|
static int generate_stable_private_address(
|
||||||
Link *link,
|
Link *link,
|
||||||
const sd_id128_t *app_id,
|
const sd_id128_t *app_id,
|
||||||
|
const sd_id128_t *secret_key,
|
||||||
const struct in6_addr *prefix,
|
const struct in6_addr *prefix,
|
||||||
struct in6_addr *ret) {
|
struct in6_addr *ret) {
|
||||||
|
|
||||||
|
sd_id128_t secret_machine_key;
|
||||||
struct in6_addr addr;
|
struct in6_addr addr;
|
||||||
sd_id128_t secret_key;
|
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(link);
|
assert(link);
|
||||||
assert(app_id);
|
assert(app_id);
|
||||||
|
assert(secret_key);
|
||||||
assert(prefix);
|
assert(prefix);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
r = sd_id128_get_machine_app_specific(*app_id, &secret_key);
|
if (sd_id128_is_null(*secret_key)) {
|
||||||
|
r = sd_id128_get_machine_app_specific(*app_id, &secret_machine_key);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_debug_errno(link, r, "Failed to generate secret key for IPv6 stable private address: %m");
|
return log_link_debug_errno(link, r, "Failed to generate secret key for IPv6 stable private address: %m");
|
||||||
|
|
||||||
|
secret_key = &secret_machine_key;
|
||||||
|
}
|
||||||
|
|
||||||
/* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop does
|
/* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop does
|
||||||
* not actually attempt Duplicate Address Detection; the counter will be incremented only when
|
* not actually attempt Duplicate Address Detection; the counter will be incremented only when
|
||||||
* the address generation algorithm produces an invalid address, and the loop may exit with an
|
* the address generation algorithm produces an invalid address, and the loop may exit with an
|
||||||
* address which ends up being unusable due to duplication on the link. */
|
* address which ends up being unusable due to duplication on the link. */
|
||||||
for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) {
|
for (i = 0; i < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; i++) {
|
||||||
generate_stable_private_address_one(link, &secret_key, prefix, i, &addr);
|
generate_stable_private_address_one(link, secret_key, prefix, i, &addr);
|
||||||
|
|
||||||
if (stable_private_address_is_valid(&addr))
|
if (stable_private_address_is_valid(&addr))
|
||||||
break;
|
break;
|
||||||
@ -192,7 +200,7 @@ static int generate_addresses(
|
|||||||
if (in6_addr_is_set(&j->address) && !in6_addr_equal(&j->address, &masked))
|
if (in6_addr_is_set(&j->address) && !in6_addr_equal(&j->address, &masked))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (generate_stable_private_address(link, app_id, &masked, &addr) < 0)
|
if (generate_stable_private_address(link, app_id, &j->secret_key, &masked, &addr) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -244,6 +252,7 @@ int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *pref
|
|||||||
static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
|
static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
|
||||||
siphash24_compress(&p->type, sizeof(p->type), state);
|
siphash24_compress(&p->type, sizeof(p->type), state);
|
||||||
siphash24_compress(&p->address, sizeof(p->address), state);
|
siphash24_compress(&p->address, sizeof(p->address), state);
|
||||||
|
id128_hash_func(&p->secret_key, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
|
static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
|
||||||
@ -253,7 +262,11 @@ static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
|
|||||||
if (r != 0)
|
if (r != 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return memcmp(&a->address, &b->address, sizeof(struct in6_addr));
|
r = memcmp(&a->address, &b->address, sizeof(struct in6_addr));
|
||||||
|
if (r != 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return id128_compare_func(&a->secret_key, &b->secret_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||||
@ -263,12 +276,13 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
|||||||
ipv6_token_compare_func,
|
ipv6_token_compare_func,
|
||||||
free);
|
free);
|
||||||
|
|
||||||
static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr) {
|
static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct in6_addr *addr, const sd_id128_t *secret_key) {
|
||||||
IPv6Token *p;
|
IPv6Token *p;
|
||||||
|
|
||||||
assert(tokens);
|
assert(tokens);
|
||||||
assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
|
assert(type >= 0 && type < _ADDRESS_GENERATION_TYPE_MAX);
|
||||||
assert(addr);
|
assert(addr);
|
||||||
|
assert(secret_key);
|
||||||
|
|
||||||
p = new(IPv6Token, 1);
|
p = new(IPv6Token, 1);
|
||||||
if (!p)
|
if (!p)
|
||||||
@ -277,6 +291,7 @@ static int ipv6_token_add(Set **tokens, AddressGenerationType type, const struct
|
|||||||
*p = (IPv6Token) {
|
*p = (IPv6Token) {
|
||||||
.type = type,
|
.type = type,
|
||||||
.address = *addr,
|
.address = *addr,
|
||||||
|
.secret_key = *secret_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
|
return set_ensure_consume(tokens, &ipv6_token_hash_ops, p);
|
||||||
@ -294,10 +309,12 @@ int config_parse_address_generation_type(
|
|||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
|
_cleanup_free_ char *addr_alloc = NULL;
|
||||||
|
sd_id128_t secret_key = SD_ID128_NULL;
|
||||||
union in_addr_union buffer = {};
|
union in_addr_union buffer = {};
|
||||||
AddressGenerationType type;
|
AddressGenerationType type;
|
||||||
Set **tokens = data;
|
Set **tokens = data;
|
||||||
const char *p;
|
const char *addr;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
assert(filename);
|
||||||
@ -310,33 +327,64 @@ int config_parse_address_generation_type(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((p = startswith(rvalue, "prefixstable"))) {
|
if ((addr = startswith(rvalue, "prefixstable"))) {
|
||||||
|
const char *comma;
|
||||||
|
|
||||||
type = ADDRESS_GENERATION_PREFIXSTABLE;
|
type = ADDRESS_GENERATION_PREFIXSTABLE;
|
||||||
|
|
||||||
if (*p == ':')
|
if (*addr == ':') {
|
||||||
p++;
|
addr++;
|
||||||
else if (*p == '\0')
|
|
||||||
p = NULL;
|
comma = strchr(addr, ',');
|
||||||
else {
|
if (comma) {
|
||||||
|
addr_alloc = strndup(addr, comma - addr);
|
||||||
|
if (!addr_alloc)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
addr = addr_alloc;
|
||||||
|
}
|
||||||
|
} else if (*addr == ',') {
|
||||||
|
comma = addr;
|
||||||
|
addr = NULL;
|
||||||
|
} else if (*addr == '\0') {
|
||||||
|
comma = NULL;
|
||||||
|
addr = NULL;
|
||||||
|
} else {
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
"Invalid IPv6 token mode in %s=, ignoring assignment: %s",
|
"Invalid IPv6 token mode in %s=, ignoring assignment: %s",
|
||||||
lvalue, rvalue);
|
lvalue, rvalue);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comma) {
|
||||||
|
r = sd_id128_from_string(comma + 1, &secret_key);
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Failed to parse secret key in %s=, ignoring assignment: %s",
|
||||||
|
lvalue, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sd_id128_is_null(secret_key)) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
|
"Secret key in %s= cannot be null, ignoring assignment: %s",
|
||||||
|
lvalue, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(rvalue, "eui64")) {
|
} else if (streq(rvalue, "eui64")) {
|
||||||
type = ADDRESS_GENERATION_EUI64;
|
type = ADDRESS_GENERATION_EUI64;
|
||||||
p = NULL;
|
addr = NULL;
|
||||||
} else {
|
} else {
|
||||||
type = ADDRESS_GENERATION_STATIC;
|
type = ADDRESS_GENERATION_STATIC;
|
||||||
|
|
||||||
p = startswith(rvalue, "static:");
|
addr = startswith(rvalue, "static:");
|
||||||
if (!p)
|
if (!addr)
|
||||||
p = rvalue;
|
addr = rvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p) {
|
if (addr) {
|
||||||
r = in_addr_from_string(AF_INET6, p, &buffer);
|
r = in_addr_from_string(AF_INET6, addr, &buffer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
"Failed to parse IP address in %s=, ignoring assignment: %s",
|
"Failed to parse IP address in %s=, ignoring assignment: %s",
|
||||||
@ -371,7 +419,7 @@ int config_parse_address_generation_type(
|
|||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
r = ipv6_token_add(tokens, type, &buffer.in6);
|
r = ipv6_token_add(tokens, type, &buffer.in6, &secret_key);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
#include "networkd-queue.h"
|
#include "networkd-queue.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "socket-netlink.h"
|
#include "socket-netlink.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
|
|||||||
@ -8,7 +8,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
|||||||
#include "networkd-conf.h"
|
#include "networkd-conf.h"
|
||||||
#include "networkd-dhcp-common.h"
|
#include "networkd-dhcp-common.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
%}
|
%}
|
||||||
struct ConfigPerfItem;
|
struct ConfigPerfItem;
|
||||||
%null_strings
|
%null_strings
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
#include "networkd-nexthop.h"
|
#include "networkd-nexthop.h"
|
||||||
#include "networkd-queue.h"
|
#include "networkd-queue.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
#include "stdio-util.h"
|
#include "stdio-util.h"
|
||||||
@ -346,7 +346,7 @@ static int nexthop_acquire_id(Manager *manager, NextHop *nexthop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) {
|
static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Link *link) {
|
||||||
_cleanup_free_ char *state = NULL, *gw = NULL, *group = NULL;
|
_cleanup_free_ char *state = NULL, *gw = NULL, *group = NULL, *flags = NULL;
|
||||||
struct nexthop_grp *nhg;
|
struct nexthop_grp *nhg;
|
||||||
|
|
||||||
assert(nexthop);
|
assert(nexthop);
|
||||||
@ -359,13 +359,14 @@ static void log_nexthop_debug(const NextHop *nexthop, const char *str, const Lin
|
|||||||
|
|
||||||
(void) network_config_state_to_string_alloc(nexthop->state, &state);
|
(void) network_config_state_to_string_alloc(nexthop->state, &state);
|
||||||
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
|
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
|
||||||
|
(void) route_flags_to_string_alloc(nexthop->flags, &flags);
|
||||||
|
|
||||||
HASHMAP_FOREACH(nhg, nexthop->group)
|
HASHMAP_FOREACH(nhg, nexthop->group)
|
||||||
(void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1);
|
(void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1);
|
||||||
|
|
||||||
log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s",
|
log_link_debug(link, "%s %s nexthop (%s): id: %"PRIu32", gw: %s, blackhole: %s, group: %s, flags: %s",
|
||||||
str, strna(network_config_source_to_string(nexthop->source)), strna(state),
|
str, strna(network_config_source_to_string(nexthop->source)), strna(state),
|
||||||
nexthop->id, strna(gw), yes_no(nexthop->blackhole), strna(group));
|
nexthop->id, strna(gw), yes_no(nexthop->blackhole), strna(group), strna(flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
#include "networkd-queue.h"
|
#include "networkd-queue.h"
|
||||||
#include "networkd-radv.h"
|
#include "networkd-radv.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "radv-internal.h"
|
#include "radv-internal.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|||||||
419
src/network/networkd-route-util.c
Normal file
419
src/network/networkd-route-util.c
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "networkd-address.h"
|
||||||
|
#include "networkd-link.h"
|
||||||
|
#include "networkd-manager.h"
|
||||||
|
#include "networkd-route-util.h"
|
||||||
|
#include "networkd-route.h"
|
||||||
|
#include "parse-util.h"
|
||||||
|
#include "string-table.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "strv.h"
|
||||||
|
#include "sysctl-util.h"
|
||||||
|
|
||||||
|
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
|
||||||
|
|
||||||
|
unsigned routes_max(void) {
|
||||||
|
static thread_local unsigned cached = 0;
|
||||||
|
_cleanup_free_ char *s4 = NULL, *s6 = NULL;
|
||||||
|
unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
||||||
|
|
||||||
|
if (cached > 0)
|
||||||
|
return cached;
|
||||||
|
|
||||||
|
if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0)
|
||||||
|
if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U)
|
||||||
|
/* This is the default "no limit" value in the kernel */
|
||||||
|
val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
||||||
|
|
||||||
|
if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0)
|
||||||
|
(void) safe_atou(s6, &val6);
|
||||||
|
|
||||||
|
cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
|
||||||
|
MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Route *link_find_default_gateway(Link *link, int family, Route *gw) {
|
||||||
|
Route *route;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
SET_FOREACH(route, link->routes) {
|
||||||
|
if (!route_exists(route))
|
||||||
|
continue;
|
||||||
|
if (family != AF_UNSPEC && route->family != family)
|
||||||
|
continue;
|
||||||
|
if (route->dst_prefixlen != 0)
|
||||||
|
continue;
|
||||||
|
if (route->src_prefixlen != 0)
|
||||||
|
continue;
|
||||||
|
if (route->table != RT_TABLE_MAIN)
|
||||||
|
continue;
|
||||||
|
if (route->type != RTN_UNICAST)
|
||||||
|
continue;
|
||||||
|
if (route->scope != RT_SCOPE_UNIVERSE)
|
||||||
|
continue;
|
||||||
|
if (!in_addr_is_set(route->gw_family, &route->gw))
|
||||||
|
continue;
|
||||||
|
if (gw) {
|
||||||
|
if (route->gw_weight > gw->gw_weight)
|
||||||
|
continue;
|
||||||
|
if (route->priority >= gw->priority)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gw = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
return gw;
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
|
||||||
|
Route *gw = NULL;
|
||||||
|
Link *link;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6));
|
||||||
|
|
||||||
|
/* Looks for a suitable "uplink", via black magic: an interface that is up and where the
|
||||||
|
* default route with the highest priority points to. */
|
||||||
|
|
||||||
|
HASHMAP_FOREACH(link, m->links_by_index) {
|
||||||
|
if (link == exclude)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (link->state != LINK_STATE_CONFIGURED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gw = link_find_default_gateway(link, family, gw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gw)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
assert(gw->link);
|
||||||
|
*ret = gw->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
|
||||||
|
Route *route;
|
||||||
|
Address *a;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(link->manager);
|
||||||
|
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||||
|
assert(address);
|
||||||
|
|
||||||
|
SET_FOREACH(route, link->routes) {
|
||||||
|
if (!route_exists(route))
|
||||||
|
continue;
|
||||||
|
if (route->family != family)
|
||||||
|
continue;
|
||||||
|
if (!in_addr_is_set(route->family, &route->dst))
|
||||||
|
continue;
|
||||||
|
if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link->manager->manage_foreign_routes)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If we do not manage foreign routes, then there may exist a prefix route we do not know,
|
||||||
|
* which was created on configuring an address. Hence, also check the addresses. */
|
||||||
|
SET_FOREACH(a, link->addresses) {
|
||||||
|
if (!address_is_ready(a))
|
||||||
|
continue;
|
||||||
|
if (a->family != family)
|
||||||
|
continue;
|
||||||
|
if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
|
||||||
|
continue;
|
||||||
|
if (in_addr_is_set(a->family, &a->in_addr_peer))
|
||||||
|
continue;
|
||||||
|
if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) {
|
||||||
|
assert(link);
|
||||||
|
assert(gw);
|
||||||
|
|
||||||
|
if (onlink)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!in_addr_is_set(family, gw))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return link_address_is_reachable(link, family, gw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const route_type_table[__RTN_MAX] = {
|
||||||
|
[RTN_UNICAST] = "unicast",
|
||||||
|
[RTN_LOCAL] = "local",
|
||||||
|
[RTN_BROADCAST] = "broadcast",
|
||||||
|
[RTN_ANYCAST] = "anycast",
|
||||||
|
[RTN_MULTICAST] = "multicast",
|
||||||
|
[RTN_BLACKHOLE] = "blackhole",
|
||||||
|
[RTN_UNREACHABLE] = "unreachable",
|
||||||
|
[RTN_PROHIBIT] = "prohibit",
|
||||||
|
[RTN_THROW] = "throw",
|
||||||
|
[RTN_NAT] = "nat",
|
||||||
|
[RTN_XRESOLVE] = "xresolve",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_cc(__RTN_MAX <= UCHAR_MAX);
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP(route_type, int);
|
||||||
|
|
||||||
|
static const char * const route_scope_table[] = {
|
||||||
|
[RT_SCOPE_UNIVERSE] = "global",
|
||||||
|
[RT_SCOPE_SITE] = "site",
|
||||||
|
[RT_SCOPE_LINK] = "link",
|
||||||
|
[RT_SCOPE_HOST] = "host",
|
||||||
|
[RT_SCOPE_NOWHERE] = "nowhere",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_scope, int, UINT8_MAX);
|
||||||
|
|
||||||
|
static const char * const route_protocol_table[] = {
|
||||||
|
[RTPROT_KERNEL] = "kernel",
|
||||||
|
[RTPROT_BOOT] = "boot",
|
||||||
|
[RTPROT_STATIC] = "static",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol, int, UINT8_MAX);
|
||||||
|
|
||||||
|
static const char * const route_protocol_full_table[] = {
|
||||||
|
[RTPROT_REDIRECT] = "redirect",
|
||||||
|
[RTPROT_KERNEL] = "kernel",
|
||||||
|
[RTPROT_BOOT] = "boot",
|
||||||
|
[RTPROT_STATIC] = "static",
|
||||||
|
[RTPROT_GATED] = "gated",
|
||||||
|
[RTPROT_RA] = "ra",
|
||||||
|
[RTPROT_MRT] = "mrt",
|
||||||
|
[RTPROT_ZEBRA] = "zebra",
|
||||||
|
[RTPROT_BIRD] = "bird",
|
||||||
|
[RTPROT_DNROUTED] = "dnrouted",
|
||||||
|
[RTPROT_XORP] = "xorp",
|
||||||
|
[RTPROT_NTK] = "ntk",
|
||||||
|
[RTPROT_DHCP] = "dhcp",
|
||||||
|
[RTPROT_MROUTED] = "mrouted",
|
||||||
|
[RTPROT_BABEL] = "babel",
|
||||||
|
[RTPROT_BGP] = "bgp",
|
||||||
|
[RTPROT_ISIS] = "isis",
|
||||||
|
[RTPROT_OSPF] = "ospf",
|
||||||
|
[RTPROT_RIP] = "rip",
|
||||||
|
[RTPROT_EIGRP] = "eigrp",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(route_protocol_full, int, UINT8_MAX);
|
||||||
|
|
||||||
|
int route_flags_to_string_alloc(uint32_t flags, char **ret) {
|
||||||
|
_cleanup_free_ char *str = NULL;
|
||||||
|
static const struct {
|
||||||
|
uint32_t flag;
|
||||||
|
const char *name;
|
||||||
|
} map[] = {
|
||||||
|
{ RTNH_F_DEAD, "dead" }, /* Nexthop is dead (used by multipath) */
|
||||||
|
{ RTNH_F_PERVASIVE, "pervasive" }, /* Do recursive gateway lookup */
|
||||||
|
{ RTNH_F_ONLINK, "onlink" }, /* Gateway is forced on link */
|
||||||
|
{ RTNH_F_OFFLOAD, "offload" }, /* Nexthop is offloaded */
|
||||||
|
{ RTNH_F_LINKDOWN, "linkdown" }, /* carrier-down on nexthop */
|
||||||
|
{ RTNH_F_UNRESOLVED, "unresolved" }, /* The entry is unresolved (ipmr) */
|
||||||
|
{ RTNH_F_TRAP, "trap" }, /* Nexthop is trapping packets */
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(map); i++)
|
||||||
|
if (flags & map[i].flag &&
|
||||||
|
!strextend_with_separator(&str, ",", map[i].name))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const route_table_table[] = {
|
||||||
|
[RT_TABLE_DEFAULT] = "default",
|
||||||
|
[RT_TABLE_MAIN] = "main",
|
||||||
|
[RT_TABLE_LOCAL] = "local",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
|
||||||
|
|
||||||
|
int manager_get_route_table_from_string(const Manager *m, const char *s, uint32_t *ret) {
|
||||||
|
uint32_t t;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(s);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
r = route_table_from_string(s);
|
||||||
|
if (r >= 0) {
|
||||||
|
*ret = (uint32_t) r;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = PTR_TO_UINT32(hashmap_get(m->route_table_numbers_by_name, s));
|
||||||
|
if (t != 0) {
|
||||||
|
*ret = t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = safe_atou32(s, &t);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (t == 0)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
*ret = t;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret) {
|
||||||
|
_cleanup_free_ char *str = NULL;
|
||||||
|
const char *s;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
if (table == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
s = route_table_to_string(table);
|
||||||
|
if (!s)
|
||||||
|
s = hashmap_get(m->route_table_names_by_number, UINT32_TO_PTR(table));
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
/* Currently, this is only used in debugging logs. To not confuse any bug
|
||||||
|
* reports, let's include the table number. */
|
||||||
|
r = asprintf(&str, "%s(%" PRIu32 ")", s, table);
|
||||||
|
else
|
||||||
|
r = asprintf(&str, "%" PRIu32, table);
|
||||||
|
if (r < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_route_table_names(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Manager *m = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(userdata);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number);
|
||||||
|
m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const char *p = rvalue;;) {
|
||||||
|
_cleanup_free_ char *name = NULL;
|
||||||
|
uint32_t table;
|
||||||
|
char *num;
|
||||||
|
|
||||||
|
r = extract_first_word(&p, &name, NULL, 0);
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Invalid RouteTable=, ignoring assignment: %s", rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (r == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
num = strchr(name, ':');
|
||||||
|
if (!num) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
|
"Invalid route table name and number pair, ignoring assignment: %s", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*num++ = '\0';
|
||||||
|
|
||||||
|
if (STR_IN_SET(name, "default", "main", "local")) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
|
"Route table name %s already predefined. Ignoring assignment: %s:%s", name, name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = safe_atou32(num, &table);
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (table == 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
|
"Invalid route table number, ignoring assignment: %s:%s", name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table));
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r == -EEXIST) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (r == 0)
|
||||||
|
/* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name);
|
||||||
|
if (r < 0) {
|
||||||
|
hashmap_remove(m->route_table_numbers_by_name, name);
|
||||||
|
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r == -EEXIST)
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
|
||||||
|
else
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(r > 0);
|
||||||
|
|
||||||
|
TAKE_PTR(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/network/networkd-route-util.h
Normal file
34
src/network/networkd-route-util.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "conf-parser.h"
|
||||||
|
|
||||||
|
typedef struct Link Link;
|
||||||
|
typedef struct Manager Manager;
|
||||||
|
|
||||||
|
unsigned routes_max(void);
|
||||||
|
|
||||||
|
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
|
||||||
|
|
||||||
|
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw);
|
||||||
|
|
||||||
|
int route_type_from_string(const char *s) _pure_;
|
||||||
|
const char *route_type_to_string(int t) _const_;
|
||||||
|
|
||||||
|
int route_scope_from_string(const char *s);
|
||||||
|
int route_scope_to_string_alloc(int t, char **ret);
|
||||||
|
|
||||||
|
int route_protocol_from_string(const char *s);
|
||||||
|
int route_protocol_to_string_alloc(int t, char **ret);
|
||||||
|
int route_protocol_full_from_string(const char *s);
|
||||||
|
int route_protocol_full_to_string_alloc(int t, char **ret);
|
||||||
|
|
||||||
|
int route_flags_to_string_alloc(uint32_t flags, char **ret);
|
||||||
|
|
||||||
|
int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret);
|
||||||
|
int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret);
|
||||||
|
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_route_table_names);
|
||||||
@ -13,166 +13,13 @@
|
|||||||
#include "networkd-network.h"
|
#include "networkd-network.h"
|
||||||
#include "networkd-nexthop.h"
|
#include "networkd-nexthop.h"
|
||||||
#include "networkd-queue.h"
|
#include "networkd-queue.h"
|
||||||
|
#include "networkd-route-util.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "string-table.h"
|
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "strxcpyx.h"
|
|
||||||
#include "sysctl-util.h"
|
|
||||||
#include "vrf.h"
|
#include "vrf.h"
|
||||||
|
|
||||||
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
|
|
||||||
|
|
||||||
static const char * const route_type_table[__RTN_MAX] = {
|
|
||||||
[RTN_UNICAST] = "unicast",
|
|
||||||
[RTN_LOCAL] = "local",
|
|
||||||
[RTN_BROADCAST] = "broadcast",
|
|
||||||
[RTN_ANYCAST] = "anycast",
|
|
||||||
[RTN_MULTICAST] = "multicast",
|
|
||||||
[RTN_BLACKHOLE] = "blackhole",
|
|
||||||
[RTN_UNREACHABLE] = "unreachable",
|
|
||||||
[RTN_PROHIBIT] = "prohibit",
|
|
||||||
[RTN_THROW] = "throw",
|
|
||||||
[RTN_NAT] = "nat",
|
|
||||||
[RTN_XRESOLVE] = "xresolve",
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_cc(__RTN_MAX <= UCHAR_MAX);
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_type, int);
|
|
||||||
|
|
||||||
static const char * const route_scope_table[] = {
|
|
||||||
[RT_SCOPE_UNIVERSE] = "global",
|
|
||||||
[RT_SCOPE_SITE] = "site",
|
|
||||||
[RT_SCOPE_LINK] = "link",
|
|
||||||
[RT_SCOPE_HOST] = "host",
|
|
||||||
[RT_SCOPE_NOWHERE] = "nowhere",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_scope, int);
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_scope, int, UINT8_MAX);
|
|
||||||
|
|
||||||
static const char * const route_table_table[] = {
|
|
||||||
[RT_TABLE_DEFAULT] = "default",
|
|
||||||
[RT_TABLE_MAIN] = "main",
|
|
||||||
[RT_TABLE_LOCAL] = "local",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
|
|
||||||
|
|
||||||
int manager_get_route_table_from_string(const Manager *m, const char *s, uint32_t *ret) {
|
|
||||||
uint32_t t;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(m);
|
|
||||||
assert(s);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
r = route_table_from_string(s);
|
|
||||||
if (r >= 0) {
|
|
||||||
*ret = (uint32_t) r;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
t = PTR_TO_UINT32(hashmap_get(m->route_table_numbers_by_name, s));
|
|
||||||
if (t != 0) {
|
|
||||||
*ret = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = safe_atou32(s, &t);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (t == 0)
|
|
||||||
return -ERANGE;
|
|
||||||
|
|
||||||
*ret = t;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret) {
|
|
||||||
_cleanup_free_ char *str = NULL;
|
|
||||||
const char *s;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(m);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
if (table == 0)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
s = route_table_to_string(table);
|
|
||||||
if (!s)
|
|
||||||
s = hashmap_get(m->route_table_names_by_number, UINT32_TO_PTR(table));
|
|
||||||
|
|
||||||
if (s)
|
|
||||||
/* Currently, this is only used in debugging logs. To not confuse any bug
|
|
||||||
* reports, let's include the table number. */
|
|
||||||
r = asprintf(&str, "%s(%" PRIu32 ")", s, table);
|
|
||||||
else
|
|
||||||
r = asprintf(&str, "%" PRIu32, table);
|
|
||||||
if (r < 0)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * const route_protocol_table[] = {
|
|
||||||
[RTPROT_KERNEL] = "kernel",
|
|
||||||
[RTPROT_BOOT] = "boot",
|
|
||||||
[RTPROT_STATIC] = "static",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(route_protocol, int, UINT8_MAX);
|
|
||||||
|
|
||||||
static const char * const route_protocol_full_table[] = {
|
|
||||||
[RTPROT_REDIRECT] = "redirect",
|
|
||||||
[RTPROT_KERNEL] = "kernel",
|
|
||||||
[RTPROT_BOOT] = "boot",
|
|
||||||
[RTPROT_STATIC] = "static",
|
|
||||||
[RTPROT_GATED] = "gated",
|
|
||||||
[RTPROT_RA] = "ra",
|
|
||||||
[RTPROT_MRT] = "mrt",
|
|
||||||
[RTPROT_ZEBRA] = "zebra",
|
|
||||||
[RTPROT_BIRD] = "bird",
|
|
||||||
[RTPROT_DNROUTED] = "dnrouted",
|
|
||||||
[RTPROT_XORP] = "xorp",
|
|
||||||
[RTPROT_NTK] = "ntk",
|
|
||||||
[RTPROT_DHCP] = "dhcp",
|
|
||||||
[RTPROT_MROUTED] = "mrouted",
|
|
||||||
[RTPROT_BABEL] = "babel",
|
|
||||||
[RTPROT_BGP] = "bgp",
|
|
||||||
[RTPROT_ISIS] = "isis",
|
|
||||||
[RTPROT_OSPF] = "ospf",
|
|
||||||
[RTPROT_RIP] = "rip",
|
|
||||||
[RTPROT_EIGRP] = "eigrp",
|
|
||||||
};
|
|
||||||
|
|
||||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_protocol_full, int, UINT8_MAX);
|
|
||||||
|
|
||||||
static unsigned routes_max(void) {
|
|
||||||
static thread_local unsigned cached = 0;
|
|
||||||
_cleanup_free_ char *s4 = NULL, *s6 = NULL;
|
|
||||||
unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
|
||||||
|
|
||||||
if (cached > 0)
|
|
||||||
return cached;
|
|
||||||
|
|
||||||
if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0)
|
|
||||||
if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U)
|
|
||||||
/* This is the default "no limit" value in the kernel */
|
|
||||||
val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
|
|
||||||
|
|
||||||
if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0)
|
|
||||||
(void) safe_atou(s6, &val6);
|
|
||||||
|
|
||||||
cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
|
|
||||||
MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
int route_new(Route **ret) {
|
int route_new(Route **ret) {
|
||||||
_cleanup_(route_freep) Route *route = NULL;
|
_cleanup_(route_freep) Route *route = NULL;
|
||||||
|
|
||||||
@ -695,115 +542,9 @@ void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
|
|
||||||
Route *route;
|
|
||||||
Address *a;
|
|
||||||
|
|
||||||
assert(link);
|
|
||||||
assert(link->manager);
|
|
||||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
|
||||||
assert(address);
|
|
||||||
|
|
||||||
SET_FOREACH(route, link->routes) {
|
|
||||||
if (!route_exists(route))
|
|
||||||
continue;
|
|
||||||
if (route->family != family)
|
|
||||||
continue;
|
|
||||||
if (!in_addr_is_set(route->family, &route->dst))
|
|
||||||
continue;
|
|
||||||
if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (link->manager->manage_foreign_routes)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* If we do not manage foreign routes, then there may exist a prefix route we do not know,
|
|
||||||
* which was created on configuring an address. Hence, also check the addresses. */
|
|
||||||
SET_FOREACH(a, link->addresses) {
|
|
||||||
if (!address_is_ready(a))
|
|
||||||
continue;
|
|
||||||
if (a->family != family)
|
|
||||||
continue;
|
|
||||||
if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
|
|
||||||
continue;
|
|
||||||
if (in_addr_is_set(a->family, &a->in_addr_peer))
|
|
||||||
continue;
|
|
||||||
if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Route *link_find_default_gateway(Link *link, int family, Route *gw) {
|
|
||||||
Route *route;
|
|
||||||
|
|
||||||
assert(link);
|
|
||||||
|
|
||||||
SET_FOREACH(route, link->routes) {
|
|
||||||
if (!route_exists(route))
|
|
||||||
continue;
|
|
||||||
if (family != AF_UNSPEC && route->family != family)
|
|
||||||
continue;
|
|
||||||
if (route->dst_prefixlen != 0)
|
|
||||||
continue;
|
|
||||||
if (route->src_prefixlen != 0)
|
|
||||||
continue;
|
|
||||||
if (route->table != RT_TABLE_MAIN)
|
|
||||||
continue;
|
|
||||||
if (route->type != RTN_UNICAST)
|
|
||||||
continue;
|
|
||||||
if (route->scope != RT_SCOPE_UNIVERSE)
|
|
||||||
continue;
|
|
||||||
if (!in_addr_is_set(route->gw_family, &route->gw))
|
|
||||||
continue;
|
|
||||||
if (gw) {
|
|
||||||
if (route->gw_weight > gw->gw_weight)
|
|
||||||
continue;
|
|
||||||
if (route->priority >= gw->priority)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
gw = route;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gw;
|
|
||||||
}
|
|
||||||
|
|
||||||
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
|
|
||||||
Route *gw = NULL;
|
|
||||||
Link *link;
|
|
||||||
|
|
||||||
assert(m);
|
|
||||||
assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
||||||
|
|
||||||
/* Looks for a suitable "uplink", via black magic: an interface that is up and where the
|
|
||||||
* default route with the highest priority points to. */
|
|
||||||
|
|
||||||
HASHMAP_FOREACH(link, m->links_by_index) {
|
|
||||||
if (link == exclude)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (link->state != LINK_STATE_CONFIGURED)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
gw = link_find_default_gateway(link, family, gw);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gw)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
assert(gw->link);
|
|
||||||
*ret = gw->link;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
|
static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
|
||||||
_cleanup_free_ char *state = NULL, *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
|
_cleanup_free_ char *state = NULL, *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
|
||||||
*table = NULL, *scope = NULL, *proto = NULL;
|
*table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
|
||||||
const char *gw = NULL;
|
const char *gw = NULL;
|
||||||
|
|
||||||
assert(route);
|
assert(route);
|
||||||
@ -851,15 +592,16 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
|
|||||||
(void) route_scope_to_string_alloc(route->scope, &scope);
|
(void) route_scope_to_string_alloc(route->scope, &scope);
|
||||||
(void) manager_get_route_table_to_string(manager, route->table, &table);
|
(void) manager_get_route_table_to_string(manager, route->table, &table);
|
||||||
(void) route_protocol_full_to_string_alloc(route->protocol, &proto);
|
(void) route_protocol_full_to_string_alloc(route->protocol, &proto);
|
||||||
|
(void) route_flags_to_string_alloc(route->flags, &flags);
|
||||||
|
|
||||||
log_link_debug(link,
|
log_link_debug(link,
|
||||||
"%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
|
"%s %s route (%s): dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, "
|
||||||
"proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
|
"proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32", flags: %s",
|
||||||
str, strna(network_config_source_to_string(route->source)), strna(state),
|
str, strna(network_config_source_to_string(route->source)), strna(state),
|
||||||
strna(dst), strna(src), strna(gw), strna(prefsrc),
|
strna(dst), strna(src), strna(gw), strna(prefsrc),
|
||||||
strna(scope), strna(table), strna(proto),
|
strna(scope), strna(table), strna(proto),
|
||||||
strna(route_type_to_string(route->type)),
|
strna(route_type_to_string(route->type)),
|
||||||
route->nexthop_id, route->priority);
|
route->nexthop_id, route->priority, strna(flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
|
static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
|
||||||
@ -1628,22 +1370,6 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) {
|
|
||||||
assert(link);
|
|
||||||
assert(gw);
|
|
||||||
|
|
||||||
if (onlink)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!in_addr_is_set(family, gw))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return link_address_is_reachable(link, family, gw);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int route_is_ready_to_configure(const Route *route, Link *link) {
|
static int route_is_ready_to_configure(const Route *route, Link *link) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -2958,112 +2684,6 @@ int config_parse_multipath_route(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_parse_route_table_names(
|
|
||||||
const char *unit,
|
|
||||||
const char *filename,
|
|
||||||
unsigned line,
|
|
||||||
const char *section,
|
|
||||||
unsigned section_line,
|
|
||||||
const char *lvalue,
|
|
||||||
int ltype,
|
|
||||||
const char *rvalue,
|
|
||||||
void *data,
|
|
||||||
void *userdata) {
|
|
||||||
|
|
||||||
Manager *m = userdata;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(filename);
|
|
||||||
assert(lvalue);
|
|
||||||
assert(rvalue);
|
|
||||||
assert(userdata);
|
|
||||||
|
|
||||||
if (isempty(rvalue)) {
|
|
||||||
m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number);
|
|
||||||
m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const char *p = rvalue;;) {
|
|
||||||
_cleanup_free_ char *name = NULL;
|
|
||||||
uint32_t table;
|
|
||||||
char *num;
|
|
||||||
|
|
||||||
r = extract_first_word(&p, &name, NULL, 0);
|
|
||||||
if (r == -ENOMEM)
|
|
||||||
return log_oom();
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Invalid RouteTable=, ignoring assignment: %s", rvalue);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (r == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
num = strchr(name, ':');
|
|
||||||
if (!num) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Invalid route table name and number pair, ignoring assignment: %s", name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
*num++ = '\0';
|
|
||||||
|
|
||||||
if (STR_IN_SET(name, "default", "main", "local")) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Route table name %s already predefined. Ignoring assignment: %s:%s", name, name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = safe_atou32(num, &table);
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (table == 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
|
||||||
"Invalid route table number, ignoring assignment: %s:%s", name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table));
|
|
||||||
if (r == -ENOMEM)
|
|
||||||
return log_oom();
|
|
||||||
if (r == -EEXIST) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (r < 0) {
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (r == 0)
|
|
||||||
/* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name);
|
|
||||||
if (r < 0) {
|
|
||||||
hashmap_remove(m->route_table_numbers_by_name, name);
|
|
||||||
|
|
||||||
if (r == -ENOMEM)
|
|
||||||
return log_oom();
|
|
||||||
if (r == -EEXIST)
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
|
|
||||||
else
|
|
||||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
|
||||||
"Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
assert(r > 0);
|
|
||||||
|
|
||||||
TAKE_PTR(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int route_section_verify(Route *route, Network *network) {
|
static int route_section_verify(Route *route, Network *network) {
|
||||||
if (section_is_invalid(route->section))
|
if (section_is_invalid(route->section))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|||||||
@ -80,8 +80,6 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li
|
|||||||
int route_remove(Route *route);
|
int route_remove(Route *route);
|
||||||
|
|
||||||
int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
|
int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
|
||||||
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
|
|
||||||
bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw);
|
|
||||||
|
|
||||||
int link_drop_routes(Link *link);
|
int link_drop_routes(Link *link);
|
||||||
int link_drop_foreign_routes(Link *link);
|
int link_drop_foreign_routes(Link *link);
|
||||||
@ -104,9 +102,6 @@ int network_add_ipv4ll_route(Network *network);
|
|||||||
int network_add_default_route_on_device(Network *network);
|
int network_add_default_route_on_device(Network *network);
|
||||||
void network_drop_invalid_routes(Network *network);
|
void network_drop_invalid_routes(Network *network);
|
||||||
|
|
||||||
int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret);
|
|
||||||
int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret);
|
|
||||||
|
|
||||||
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
|
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
|
||||||
void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router);
|
void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router);
|
||||||
|
|
||||||
@ -124,5 +119,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window);
|
|||||||
CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
|
CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
|
CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss);
|
CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_route_table_names);
|
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
|
CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
#include "netlink-util.h"
|
#include "netlink-util.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
#include "networkd-queue.h"
|
#include "networkd-queue.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
#include "networkd-routing-policy-rule.h"
|
#include "networkd-routing-policy-rule.h"
|
||||||
#include "networkd-util.h"
|
#include "networkd-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
#include "network-internal.h"
|
#include "network-internal.h"
|
||||||
#include "networkd-address.h"
|
#include "networkd-address.h"
|
||||||
#include "networkd-manager.h"
|
#include "networkd-manager.h"
|
||||||
#include "networkd-route.h"
|
#include "networkd-route-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "tests.h"
|
#include "tests.h"
|
||||||
|
|||||||
@ -565,7 +565,7 @@ static int link_is_managed(Link *l) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
return !STR_IN_SET(state, "pending", "unmanaged");
|
return !STR_IN_SET(state, "pending", "initialized", "unmanaged");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void link_read_settings(Link *l) {
|
static void link_read_settings(Link *l) {
|
||||||
|
|||||||
@ -7,3 +7,4 @@ IPv6AcceptRA=true
|
|||||||
|
|
||||||
[IPv6AcceptRA]
|
[IPv6AcceptRA]
|
||||||
Token=prefixstable
|
Token=prefixstable
|
||||||
|
Token=prefixstable,86b123b969ba4b7eb8b3d8605123525a
|
||||||
|
|||||||
@ -7,7 +7,12 @@ IPv6AcceptRA=true
|
|||||||
|
|
||||||
[IPv6AcceptRA]
|
[IPv6AcceptRA]
|
||||||
Token=prefixstable:2002:da8:1::
|
Token=prefixstable:2002:da8:1::
|
||||||
|
Token=prefixstable:2002:da8:1::,86b123b969ba4b7eb8b3d8605123525a
|
||||||
# invalid tokens
|
# invalid tokens
|
||||||
|
Token=prefixstable:2002:da8:1::,00000000000000000000000000000000
|
||||||
|
Token=prefixstable:2002:da8:1::,
|
||||||
|
Token=prefixstable,00000000000000000000000000000000
|
||||||
|
Token=prefixstable,
|
||||||
Token=prefixstable@
|
Token=prefixstable@
|
||||||
Token=static
|
Token=static
|
||||||
Token=static:
|
Token=static:
|
||||||
|
|||||||
@ -3951,8 +3951,8 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
|||||||
|
|
||||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||||
print(output)
|
print(output)
|
||||||
self.assertRegex(output, '2002:da8:1:0')
|
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
|
||||||
self.assertRegex(output, '2002:da8:2:0.*78:9abc') # EUI
|
self.assertIn('2002:da8:2:0:1034:56ff:fe78:9abc', output) # EUI64
|
||||||
|
|
||||||
def test_ipv6_token_prefixstable_without_address(self):
|
def test_ipv6_token_prefixstable_without_address(self):
|
||||||
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable-without-address.network')
|
copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth-token-prefixstable-without-address.network')
|
||||||
@ -3961,8 +3961,8 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
|||||||
|
|
||||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||||
print(output)
|
print(output)
|
||||||
self.assertRegex(output, '2002:da8:1:0')
|
self.assertIn('2002:da8:1:0:b47e:7975:fc7a:7d6e', output)
|
||||||
self.assertRegex(output, '2002:da8:2:0')
|
self.assertIn('2002:da8:2:0:f689:561a:8eda:7443', output)
|
||||||
|
|
||||||
class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
|
class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
|
||||||
links = [
|
links = [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user