Compare commits

..

15 Commits

Author SHA1 Message Date
Kevin Kuehler 097537f07a job: Don't mark as redundant if deps are relevant
In the steps given in #13850, the resulting graph looks like:

    C (Anchor) -> B -> A

Since B is inactive, it will be flagged as redundant and removed from
the transaction, causing A to get garbage collected. The proposed fix is
to not mark nodes as redundant if doing so causes a relevant node to be
garbage collected.

Fixes #13850
2020-01-03 15:58:10 +01:00
Lennart Poettering dc5737470e
Merge pull request #14194 from yuwata/network-multipath-routing-12541
network: introduce multipath routing
2020-01-03 15:38:03 +01:00
Lennart Poettering a9586f0ca8
Merge pull request #14469 from aburch/man-tmpfiles.d-minor-improvements
man: tmpfiles.d: minor improvements
2020-01-03 14:18:16 +01:00
Lennart Poettering 12da859a3f
Merge pull request #14401 from DaanDeMeyer/nspawn-move-veth-back-to-host
nspawn: move virtual interfaces added with --network-interface back to the host
2020-01-03 12:47:03 +01:00
Ansgar Burchardt 72a86dd5ec man: tmpfiles.d: only list "v" once 2020-01-03 20:22:48 +09:00
Ansgar Burchardt f6bc26ee7f man: tmpfiles.d: "b", "c" options require major and minor numbers 2020-01-03 20:22:31 +09:00
Mike Auty 2ceefe4587 hwdb: Lenovo T490 Synaptics Touchpad hwdb entry
The Synaptics Touchpad that comes with the T490 doesn't explicitly set
its resolution, so these lines are needed to provide that, in order to
help the libinput code detect overly large jumps.  Since this device
contains buttons under the lower section of the touchpad, large jumps
are common, so having the resolution helps libinput greatly reduce
the number of occurances of pointer jump.

This comes from
https://gitlab.freedesktop.org/libinput/libinput/issues/402.
2020-01-03 09:57:07 +01:00
Lennart Poettering eb253fbdbd
Merge pull request #14467 from poettering/nspawn-short-names-rework
nspawn: change how we truncate --network-veth names
2020-01-03 09:55:29 +01:00
Kai Krakow bc5ea049f2 nspawn: Generate unique short veth names
This commit lowers the chance of having veth name conflicts for machines
created with similar names.

Replaces: #12865
Fixes: #13417
2020-01-02 20:05:42 +01:00
Lennart Poettering b355d0c9af udev: move naming-scheme.[ch] into src/shared/
That way we can use it from non-udev code too, for example nspawn.
2020-01-02 19:34:00 +01:00
Daan De Meyer 5b4855ab73 nspawn: Move --network-interface interfaces back to the host. 2020-01-02 14:13:03 +01:00
Daan De Meyer b390f17892 nspawn-network: Split off udev checking from parse_interface. 2019-12-23 18:47:36 +01:00
Yu Watanabe a0ce990e71 test-network: add test case for multipath routing 2019-12-18 22:12:57 +09:00
Yu Watanabe 6ff5cc6b7a network: introduce multipath route
Closes #12541.
2019-12-18 22:12:57 +09:00
Yu Watanabe 6497a8aa9b sd-netlink: introduce rtattr_append_attribute()
It will be used in later commit.
2019-12-18 22:12:57 +09:00
27 changed files with 584 additions and 101 deletions

View File

@ -514,6 +514,13 @@ evdev:name:AlpsPS/2 ALPS GlidePoint:dmi:*svnLENOVO:*pvrLenovoYoga500-14IBD*
EVDEV_ABS_35=117:3952:36
EVDEV_ABS_36=105:1960:26
# Lenovo Thinkpad T490
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*:svnLENOVO:*pvrThinkPadT490:*
EVDEV_ABS_00=::57
EVDEV_ABS_01=::33
EVDEV_ABS_35=::57
EVDEV_ABS_36=::33
#########################################
# Razer
#########################################

View File

@ -835,7 +835,11 @@
container names may have a length up to 64 characters. As this option derives the host-side interface
name from the container name the name is possibly truncated. Thus, care needs to be taken to ensure
that interface names remain unique in this case, or even better container names are generally not
chosen longer than 12 characters, to avoid the truncation. Alternatively, the
chosen longer than 12 characters, to avoid the truncation. If the name is truncated,
<command>systemd-nspawn</command> will automatically append a 4-digit hash value to the name to
reduce the chance of collisions. However, the hash algorithm is not collision-free. (See
<citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details on older naming algorithms for this interface). Alternatively, the
<option>--network-veth-extra=</option> option may be used, which allows free configuration of the
host-side interface name independently of the container name — but might require a bit more
additional configuration in case bridging in a fashion similar to <option>--network-bridge=</option>

View File

@ -46,6 +46,11 @@
devices based on those properties. See the description of <varname>NamePolicy=</varname> and
<varname>MACAddressPolicy=</varname> in
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>Note that while the concept of network interface naming schemes is primarily relevant in the
context of <filename>systemd-udevd.service</filename>, the
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
container manager also takes it into account when naming network interfaces, see below.</para>
</refsect1>
<refsect1>
@ -329,7 +334,21 @@
<para>Previously two-letter interface type prefix was prepended to
<varname>ID_NET_LABEL_ONBOARD=</varname>. This is not done anymore.</para></listitem>
</varlistentry>
</variablelist>
<varlistentry>
<term><constant>v245</constant></term>
<listitem><para>When
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
derives the name for the host side of the network interface created with
<option>--network-veth</option> from the container name it previously simply truncated the result
at 15 characters if longer (since that's the maximum length for network interface names). From now
on, for any interface name that would be longer than 15 characters the last 4 characters are set to
a 24bit hash value of the full interface name. This way network interface name collisions between
multiple similarly named containers (who only differ in container name suffix) should be less
likely (but still possible, since the 24bit hash value is very small).</para></listitem>
</varlistentry>
</variablelist>
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
particular version of systemd.</para>
@ -428,7 +447,8 @@ ID_NET_NAME_PATH=encf5f0</programlisting>
<para>
<citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<ulink url="https://systemd.io/PREDICTABLE_INTERFACE_NAMES">Predictable Network Interface Names</ulink>
<ulink url="https://systemd.io/PREDICTABLE_INTERFACE_NAMES">Predictable Network Interface Names</ulink>,
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -1378,6 +1378,16 @@
service type to CS6 (network control) or CS4 (Realtime). Defaults to CS6.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MultiPathRoute=<replaceable>address</replaceable>[@<replaceable>name</replaceable>] [<replaceable>weight</replaceable>]</varname></term>
<listitem>
<para>Configures multipath route. Multipath routing is the technique of using multiple
alternative paths through a network. Takes gateway address. Optionally, takes a network
interface name or index separated with <literal>@</literal>, and a weight in 1..256 for
this multipath route separated with whitespace. This setting can be specified multiple
times. If an empty string is assigned, then the all previous assignments are cleared.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -45,17 +45,16 @@ w+ /file/to/append-to - - - - conte
d /directory/to/create-and-cleanup mode user group cleanup-age -
D /directory/to/create-and-remove mode user group cleanup-age -
e /directory/to/cleanup mode user group cleanup-age -
v /subvolume/to/create mode user group - -
v /subvolume-or-directory/to/create mode user group - -
Q /subvolume/to/create mode user group - -
p /fifo/to/create mode user group - -
p+ /fifo/to/[re]create mode user group - -
L /symlink/to/create - - - - symlink/target/path
L+ /symlink/to/[re]create - - - - symlink/target/path
c /dev/char-device-to-create mode user group - -
c+ /dev/char-device-to-[re]create mode user group - -
b /dev/block-device-to-create mode user group - -
b+ /dev/block-device-to-[re]create mode user group - -
c /dev/char-device-to-create mode user group - major:minor
c+ /dev/char-device-to-[re]create mode user group - major:minor
b /dev/block-device-to-create mode user group - major:minor
b+ /dev/block-device-to-[re]create mode user group - major:minor
C /target/to/create - - - - /source/to/copy
x /path-or-glob/to/ignore - - - - -
X /path-or-glob/to/ignore/recursively - - - - -

View File

@ -383,25 +383,62 @@ JobType job_type_lookup_merge(JobType a, JobType b) {
return job_merging_table[(a - 1) * a / 2 + b];
}
bool job_type_is_redundant(JobType a, UnitActiveState b) {
switch (a) {
bool job_later_link_matters(Job *j, JobType type, unsigned generation) {
JobDependency *l;
assert(j);
j->generation = generation;
LIST_FOREACH(subject, l, j->subject_list) {
UnitActiveState state = _UNIT_ACTIVE_STATE_INVALID;
/* Have we seen this before? */
if (l->object->generation == generation)
continue;
state = unit_active_state(l->object->unit);
switch (type) {
case JOB_START:
return IN_SET(state, UNIT_INACTIVE, UNIT_FAILED) ||
job_later_link_matters(l->object, type, generation);
case JOB_STOP:
return IN_SET(state, UNIT_ACTIVE, UNIT_RELOADING) ||
job_later_link_matters(l->object, type, generation);
default:
assert_not_reached("Invalid job type");
}
}
return false;
}
bool job_is_redundant(Job *j, unsigned generation) {
assert(j);
UnitActiveState state = unit_active_state(j->unit);
switch (j->type) {
case JOB_START:
return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
return IN_SET(state, UNIT_ACTIVE, UNIT_RELOADING) && !job_later_link_matters(j, JOB_START, generation);
case JOB_STOP:
return IN_SET(b, UNIT_INACTIVE, UNIT_FAILED);
return IN_SET(state, UNIT_INACTIVE, UNIT_FAILED) && !job_later_link_matters(j, JOB_STOP, generation);
case JOB_VERIFY_ACTIVE:
return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
return IN_SET(state, UNIT_ACTIVE, UNIT_RELOADING);
case JOB_RELOAD:
return
b == UNIT_RELOADING;
state == UNIT_RELOADING;
case JOB_RESTART:
return
b == UNIT_ACTIVATING;
state == UNIT_ACTIVATING;
case JOB_NOP:
return true;

View File

@ -196,7 +196,8 @@ _pure_ static inline bool job_type_is_superset(JobType a, JobType b) {
return a == job_type_lookup_merge(a, b);
}
bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_;
bool job_later_link_matters(Job *j, JobType type, unsigned generation);
bool job_is_redundant(Job *j, unsigned generation);
/* Collapses a state-dependent job type into a simpler type by observing
* the state of the unit which it is going to be applied to. */

View File

@ -279,7 +279,7 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
return 0;
}
static void transaction_drop_redundant(Transaction *tr) {
static void transaction_drop_redundant(Transaction *tr, unsigned generation) {
bool again;
/* Goes through the transaction and removes all jobs of the units whose jobs are all noops. If not
@ -299,7 +299,7 @@ static void transaction_drop_redundant(Transaction *tr) {
LIST_FOREACH(transaction, k, j)
if (tr->anchor_job == k ||
!job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
!job_is_redundant(k, generation) ||
(k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) {
keep = true;
break;
@ -730,7 +730,7 @@ int transaction_activate(
transaction_minimize_impact(tr);
/* Third step: Drop redundant jobs */
transaction_drop_redundant(tr);
transaction_drop_redundant(tr, generation++);
for (;;) {
/* Fourth step: Let's remove unneeded jobs that might
@ -772,7 +772,7 @@ int transaction_activate(
}
/* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
transaction_drop_redundant(tr);
transaction_drop_redundant(tr, generation++);
/* Ninth step: check whether we can actually apply this */
r = transaction_is_destructive(tr, mode, e);

View File

@ -142,11 +142,9 @@ int sd_netlink_message_is_broadcast(const sd_netlink_message *m) {
/* If successful the updated message will be correctly aligned, if
unsuccessful the old message is untouched. */
static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) {
uint32_t rta_length;
size_t message_length, padding_length;
size_t message_length;
struct nlmsghdr *new_hdr;
struct rtattr *rta;
char *padding;
unsigned i;
int offset;
@ -154,16 +152,10 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
assert(m->hdr);
assert(!m->sealed);
assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len);
assert(!data || data_length);
/* get offset of the new attribute */
offset = m->hdr->nlmsg_len;
/* get the size of the new rta attribute (with padding at the end) */
rta_length = RTA_LENGTH(data_length);
assert(!data || data_length > 0);
/* get the new message size (with padding at the end) */
message_length = offset + RTA_ALIGN(rta_length);
message_length = m->hdr->nlmsg_len + RTA_SPACE(data_length);
/* buffer should be smaller than both one page or 8K to be accepted by the kernel */
if (message_length > MIN(page_size(), 8192UL))
@ -176,33 +168,19 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
m->hdr = new_hdr;
/* get pointer to the attribute we are about to add */
rta = (struct rtattr *) ((uint8_t *) m->hdr + offset);
rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len);
rtattr_append_attribute_internal(rta, type, data, data_length);
/* if we are inside containers, extend them */
for (i = 0; i < m->n_containers; i++)
GET_CONTAINER(m, i)->rta_len += message_length - offset;
/* fill in the attribute */
rta->rta_type = type;
rta->rta_len = rta_length;
if (data)
/* we don't deal with the case where the user lies about the type
* and gives us too little data (so don't do that)
*/
padding = mempcpy(RTA_DATA(rta), data, data_length);
else
/* if no data was passed, make sure we still initialize the padding
note that we can have data_length > 0 (used by some containers) */
padding = RTA_DATA(rta);
/* make sure also the padding at the end of the message is initialized */
padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding;
memzero(padding, padding_length);
GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length);
/* update message size */
offset = m->hdr->nlmsg_len;
m->hdr->nlmsg_len = message_length;
/* return old message size */
return offset;
}

View File

@ -2,6 +2,7 @@
#include "sd-netlink.h"
#include "memory-util.h"
#include "netlink-internal.h"
#include "netlink-util.h"
#include "strv.h"
@ -178,3 +179,60 @@ int rtnl_log_parse_error(int r) {
int rtnl_log_create_error(int r) {
return log_error_errno(r, "Failed to create netlink message: %m");
}
void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) {
size_t padding_length;
char *padding;
assert(rta);
assert(!data || data_length > 0);
/* fill in the attribute */
rta->rta_type = type;
rta->rta_len = RTA_LENGTH(data_length);
if (data)
/* we don't deal with the case where the user lies about the type
* and gives us too little data (so don't do that)
*/
padding = mempcpy(RTA_DATA(rta), data, data_length);
else
/* if no data was passed, make sure we still initialize the padding
note that we can have data_length > 0 (used by some containers) */
padding = RTA_DATA(rta);
/* make sure also the padding at the end of the message is initialized */
padding_length = (char *) rta + RTA_SPACE(data_length) - padding;
memzero(padding, padding_length);
}
int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) {
struct rtattr *new_rta, *sub_rta;
size_t message_length;
assert(rta);
assert(!data || data_length > 0);
/* get the new message size (with padding at the end) */
message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length);
/* buffer should be smaller than both one page or 8K to be accepted by the kernel */
if (message_length > MIN(page_size(), 8192UL))
return -ENOBUFS;
/* realloc to fit the new attribute */
new_rta = realloc(*rta, message_length);
if (!new_rta)
return -ENOMEM;
*rta = new_rta;
/* get pointer to the attribute we are about to add */
sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len));
rtattr_append_attribute_internal(sub_rta, type, data, data_length);
/* update rta_len */
(*rta)->rta_len = message_length;
return 0;
}

View File

@ -77,3 +77,6 @@ int rtnl_log_create_error(int r);
int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data);
int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data);
void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length);
int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length);

View File

@ -1040,7 +1040,7 @@ int link_request_set_routes(Link *link) {
for (phase = 0; phase < _PHASE_MAX; phase++)
LIST_FOREACH(routes, rt, link->network->static_routes) {
if (in_addr_is_null(rt->family, &rt->gw) != (phase == PHASE_NON_GATEWAY))
if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
continue;
r = route_configure(rt, link, route_handler);

View File

@ -147,6 +147,7 @@ Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window,
Route.QuickAck, config_parse_quickack, 0, 0
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0
Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
Route.MultiPathRoute, config_parse_multipath_route, 0, 0
NextHop.Id, config_parse_nexthop_id, 0, 0
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)

View File

@ -144,6 +144,8 @@ void route_free(Route *route) {
set_remove(route->link->routes_foreign, route);
}
ordered_set_free_free(route->multipath_routes);
sd_event_source_unref(route->expire);
free(route);
@ -516,6 +518,88 @@ int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
return 1;
}
static int append_nexthop_one(Route *route, MultipathRoute *m, struct rtattr **rta, size_t offset) {
struct rtnexthop *rtnh;
struct rtattr *new_rta;
int r;
assert(route);
assert(m);
assert(rta);
assert(*rta);
new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
if (!new_rta)
return -ENOMEM;
*rta = new_rta;
rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
*rtnh = (struct rtnexthop) {
.rtnh_len = sizeof(*rtnh),
.rtnh_ifindex = m->ifindex,
.rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
};
(*rta)->rta_len += sizeof(struct rtnexthop);
if (route->family == m->gateway.family) {
r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
if (r < 0)
goto clear;
rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
} else {
r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
if (r < 0)
goto clear;
rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
}
return 0;
clear:
(*rta)->rta_len -= sizeof(struct rtnexthop);
return r;
}
static int append_nexthops(Route *route, sd_netlink_message *req) {
_cleanup_free_ struct rtattr *rta = NULL;
struct rtnexthop *rtnh;
MultipathRoute *m;
size_t offset;
Iterator i;
int r;
if (ordered_set_isempty(route->multipath_routes))
return 0;
rta = new(struct rtattr, 1);
if (!rta)
return -ENOMEM;
*rta = (struct rtattr) {
.rta_type = RTA_MULTIPATH,
.rta_len = RTA_LENGTH(0),
};
offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
ORDERED_SET_FOREACH(m, route->multipath_routes, i) {
r = append_nexthop_one(route, m, &rta, offset);
if (r < 0)
return r;
rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
}
r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
if (r < 0)
return r;
return 0;
}
int route_configure(
Route *route,
Link *link,
@ -699,6 +783,10 @@ int route_configure(
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
r = append_nexthops(route, req);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
@ -1480,6 +1568,113 @@ int config_parse_route_ttl_propagate(
return 0;
}
int config_parse_multipath_route(
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) {
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
_cleanup_free_ char *word = NULL, *buf = NULL;
_cleanup_free_ MultipathRoute *m = NULL;
Network *network = userdata;
const char *p, *ip, *dev;
union in_addr_union a;
int family, r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = route_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
if (isempty(rvalue)) {
n->multipath_routes = ordered_set_free_free(n->multipath_routes);
return 0;
}
m = new0(MultipathRoute, 1);
if (!m)
return log_oom();
p = rvalue;
r = extract_first_word(&p, &word, NULL, 0);
if (r == -ENOMEM)
return log_oom();
if (r <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid multipath route option, ignoring assignment: %s", rvalue);
return 0;
}
dev = strchr(word, '@');
if (dev) {
buf = strndup(word, dev - word);
if (!buf)
return log_oom();
ip = buf;
dev++;
} else
ip = word;
r = in_addr_from_string_auto(ip, &family, &a);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
return 0;
}
m->gateway.address = a;
m->gateway.family = family;
if (dev) {
r = parse_ifindex_or_ifname(dev, &m->ifindex);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid interface name or index, ignoring assignment: %s", dev);
return 0;
}
}
if (!isempty(p)) {
r = safe_atou32(p, &m->weight);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid multipath route weight, ignoring assignment: %s", p);
return 0;
}
if (m->weight == 0 || m->weight > 256) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid multipath route weight, ignoring assignment: %s", p);
return 0;
}
}
r = ordered_set_ensure_allocated(&n->multipath_routes, NULL);
if (r < 0)
return log_oom();
r = ordered_set_put(n->multipath_routes, m);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to store multipath route, ignoring assignment: %m");
return 0;
}
TAKE_PTR(m);
TAKE_PTR(n);
return 0;
}
int route_section_verify(Route *route, Network *network) {
if (section_is_invalid(route->section))
return -EINVAL;

View File

@ -10,6 +10,17 @@ typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
#include "networkd-util.h"
typedef struct MultipathRouteVia {
uint16_t family;
union in_addr_union address;
} _packed_ MultipathRouteVia;
typedef struct MultipathRoute {
MultipathRouteVia gateway;
int ifindex;
uint32_t weight;
} MultipathRoute;
struct Route {
Network *network;
NetworkConfigSection *section;
@ -42,6 +53,7 @@ struct Route {
union in_addr_union dst;
union in_addr_union src;
union in_addr_union prefsrc;
OrderedSet *multipath_routes;
usec_t lifetime;
sd_event_source *expire;
@ -96,3 +108,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_quickack);
CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie);
CONFIG_PARSER_PROTOTYPE(config_parse_route_ttl_propagate);
CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);

View File

@ -11,6 +11,7 @@
#include "ether-addr-util.h"
#include "lockfile-util.h"
#include "missing_network.h"
#include "netif-naming-scheme.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "parse-util.h"
@ -27,6 +28,7 @@
#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66)
#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
#define SHORTEN_IFNAME_HASH_KEY SD_ID128_MAKE(e1,90,a4,04,a8,ef,4b,51,8c,cc,c3,3a,9f,11,fc,a2)
static int remove_one_link(sd_netlink *rtnl, const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
@ -169,6 +171,48 @@ static int add_veth(
return 0;
}
/* This is almost base64char(), but not entirely, as it uses the "url and filename safe" alphabet, since we
* don't want "/" appear in interface names (since interfaces appear in sysfs as filenames). See section #5
* of RFC 4648. */
static char urlsafe_base64char(int x) {
static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return table[x & 63];
}
static void shorten_ifname(char *ifname) {
char new_ifname[IFNAMSIZ];
assert(ifname);
if (strlen(ifname) < IFNAMSIZ) /* Name is short enough */
return;
if (naming_scheme_has(NAMING_NSPAWN_LONG_HASH)) {
uint64_t h;
/* Calculate 64bit hash value */
h = siphash24(ifname, strlen(ifname), SHORTEN_IFNAME_HASH_KEY.bytes);
/* Set the final four bytes (i.e. 32bit) to the lower 24bit of the hash, encoded in url-safe base64 */
memcpy(new_ifname, ifname, IFNAMSIZ - 5);
new_ifname[IFNAMSIZ - 5] = urlsafe_base64char(h >> 18);
new_ifname[IFNAMSIZ - 4] = urlsafe_base64char(h >> 12);
new_ifname[IFNAMSIZ - 3] = urlsafe_base64char(h >> 6);
new_ifname[IFNAMSIZ - 2] = urlsafe_base64char(h);
} else
/* On old nspawn versions we just truncated the name, provide compatibility */
memcpy(new_ifname, ifname, IFNAMSIZ-1);
new_ifname[IFNAMSIZ - 1] = 0;
/* Log the incident to make it more discoverable */
log_warning("Network interface name '%s' has been changed to '%s' to fit length constraints.", ifname, new_ifname);
strcpy(ifname, new_ifname);
}
int setup_veth(const char *machine_name,
pid_t pid,
char iface_name[IFNAMSIZ],
@ -176,7 +220,9 @@ int setup_veth(const char *machine_name,
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
struct ether_addr mac_host, mac_container;
int r, i;
unsigned u;
char *n;
int r;
assert(machine_name);
assert(pid > 0);
@ -184,8 +230,8 @@ int setup_veth(const char *machine_name,
/* Use two different interface name prefixes depending whether
* we are in bridge mode or not. */
snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
bridge ? "vb" : "ve", machine_name);
n = strjoina(bridge ? "vb-" : "ve-", machine_name);
shorten_ifname(n);
r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
if (r < 0)
@ -199,15 +245,16 @@ int setup_veth(const char *machine_name,
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container);
r = add_veth(rtnl, pid, n, &mac_host, "host0", &mac_container);
if (r < 0)
return r;
r = parse_ifindex_or_ifname(iface_name, &i);
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s: %m", iface_name);
u = if_nametoindex(n);
if (u == 0)
return log_error_errno(errno, "Failed to resolve interface %s: %m", n);
return i;
strcpy(iface_name, n);
return (int) u;
}
int setup_veth_extra(
@ -395,40 +442,50 @@ int remove_bridge(const char *bridge_name) {
}
static int parse_interface(const char *name) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
int ifi, r;
r = parse_ifindex_or_ifname(name, &ifi);
if (r < 0)
return log_error_errno(r, "Failed to resolve interface %s: %m", name);
if (path_is_read_only_fs("/sys") <= 0) {
char ifi_str[2 + DECIMAL_STR_MAX(int)];
/* udev should be around. */
sprintf(ifi_str, "n%i", ifi);
r = sd_device_new_from_device_id(&d, ifi_str);
if (r < 0)
return log_error_errno(r, "Failed to get device %s: %m", name);
r = sd_device_get_is_initialized(d);
if (r < 0)
return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name);
r = device_is_renaming(d);
if (r < 0)
return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name);
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name);
}
return ifi;
}
int move_network_interfaces(pid_t pid, char **ifaces) {
int test_network_interface_initialized(const char *name) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
int ifi, r;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
if (path_is_read_only_fs("/sys"))
return 0;
/* udev should be around. */
ifi = parse_interface(name);
if (ifi < 0)
return ifi;
sprintf(ifi_str, "n%i", ifi);
r = sd_device_new_from_device_id(&d, ifi_str);
if (r < 0)
return log_error_errno(r, "Failed to get device %s: %m", name);
r = sd_device_get_is_initialized(d);
if (r < 0)
return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name);
r = device_is_renaming(d);
if (r < 0)
return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name);
if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name);
return 0;
}
int move_network_interfaces(int netns_fd, char **ifaces) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char **i;
int r;
@ -452,9 +509,9 @@ int move_network_interfaces(pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to allocate netlink message: %m");
r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid);
r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
if (r < 0)
return log_error_errno(r, "Failed to append namespace PID to netlink message: %m");
return log_error_errno(r, "Failed to append namespace fd to netlink message: %m");
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
@ -503,7 +560,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (!n)
return log_oom();
strshorten(n, IFNAMSIZ-1);
shorten_ifname(n);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
@ -578,7 +635,7 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (!n)
return log_oom();
strshorten(n, IFNAMSIZ-1);
shorten_ifname(n);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)

View File

@ -5,6 +5,8 @@
#include <stdbool.h>
#include <sys/types.h>
int test_network_interface_initialized(const char *name);
int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
@ -14,7 +16,7 @@ int remove_bridge(const char *bridge_name);
int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces);
int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
int move_network_interfaces(pid_t pid, char **ifaces);
int move_network_interfaces(int netns_fd, char **ifaces);
int veth_extra_parse(char ***l, const char *p);

View File

@ -848,6 +848,10 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Network interface name not valid: %s", optarg);
r = test_network_interface_initialized(optarg);
if (r < 0)
return r;
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
@ -861,6 +865,10 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"MACVLAN network interface name not valid: %s", optarg);
r = test_network_interface_initialized(optarg);
if (r < 0)
return r;
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
@ -874,6 +882,10 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"IPVLAN network interface name not valid: %s", optarg);
r = test_network_interface_initialized(optarg);
if (r < 0)
return r;
if (strv_extend(&arg_network_ipvlan, optarg) < 0)
return log_oom();
@ -4199,7 +4211,7 @@ static int run_container(
int ifi = 0, r;
ssize_t l;
sigset_t mask_chld;
_cleanup_close_ int netns_fd = -1;
_cleanup_close_ int child_netns_fd = -1;
assert_se(sigemptyset(&mask_chld) == 0);
assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
@ -4258,11 +4270,11 @@ static int run_container(
return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
if (arg_network_namespace_path) {
netns_fd = open(arg_network_namespace_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (netns_fd < 0)
child_netns_fd = open(arg_network_namespace_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (child_netns_fd < 0)
return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path);
r = fd_is_network_ns(netns_fd);
r = fd_is_network_ns(child_netns_fd);
if (r == -EUCLEAN)
log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path);
else if (r < 0)
@ -4307,7 +4319,7 @@ static int run_container(
master_pty_socket_pair[1],
unified_cgroup_hierarchy_socket_pair[1],
fds,
netns_fd);
child_netns_fd);
if (r < 0)
_exit(EXIT_FAILURE);
@ -4409,7 +4421,15 @@ static int run_container(
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early");
}
r = move_network_interfaces(*pid, arg_network_interfaces);
if (child_netns_fd < 0) {
/* Make sure we have an open file descriptor to the child's network
* namespace so it stays alive even if the child exits. */
r = namespace_open(*pid, NULL, NULL, &child_netns_fd, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to open child network namespace: %m");
}
r = move_network_interfaces(child_netns_fd, arg_network_interfaces);
if (r < 0)
return r;
@ -4655,6 +4675,36 @@ static int run_container(
/* Normally redundant, but better safe than sorry */
(void) kill(*pid, SIGKILL);
if (arg_private_network) {
/* Move network interfaces back to the parent network namespace. We use `safe_fork`
* to avoid having to move the parent to the child network namespace. */
r = safe_fork(NULL, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL);
if (r < 0)
return r;
if (r == 0) {
_cleanup_close_ int parent_netns_fd = -1;
r = namespace_open(getpid(), NULL, NULL, &parent_netns_fd, NULL, NULL);
if (r < 0) {
log_error_errno(r, "Failed to open parent network namespace: %m");
_exit(EXIT_FAILURE);
}
r = namespace_enter(-1, -1, child_netns_fd, -1, -1);
if (r < 0) {
log_error_errno(r, "Failed to enter child network namespace: %m");
_exit(EXIT_FAILURE);
}
r = move_network_interfaces(parent_netns_fd, arg_network_interfaces);
if (r < 0)
log_error_errno(r, "Failed to move network interfaces back to parent network namespace: %m");
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
}
r = wait_for_container(*pid, &container_status);
*pid = 0;

View File

@ -130,6 +130,8 @@ shared_sources = files('''
module-util.h
mount-util.c
mount-util.h
netif-naming-scheme.c
netif-naming-scheme.h
nscd-flush.c
nscd-flush.h
nsflags.c

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "naming-scheme.h"
#include "netif-naming-scheme.h"
#include "proc-cmdline.h"
#include "string-util.h"
@ -10,6 +11,7 @@ static const NamingScheme naming_schemes[] = {
{ "v240", NAMING_V240 },
{ "v241", NAMING_V241 },
{ "v243", NAMING_V243 },
{ "v245", NAMING_V245 },
/* … add more schemes here, as the logic to name devices is updated … */
};

View File

@ -30,6 +30,7 @@ typedef enum NamingSchemeFlags {
NAMING_STABLE_VIRTUAL_MACS = 1 << 5, /* Use device name to generate MAC, see 6d3646406560 */
NAMING_NETDEVSIM = 1 << 6, /* Generate names for netdevsim devices, see eaa9d507d855 */
NAMING_LABEL_NOPREFIX = 1 << 7, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
NAMING_NSPAWN_LONG_HASH = 1 << 8, /* Shorten nspawn interfaces by including 24bit hash, instead of simple truncation */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
@ -37,6 +38,7 @@ typedef enum NamingSchemeFlags {
NAMING_V240 = NAMING_V239 | NAMING_INFINIBAND | NAMING_ZERO_ACPI_INDEX | NAMING_ALLOW_RERENAMES,
NAMING_V241 = NAMING_V240 | NAMING_STABLE_VIRTUAL_MACS,
NAMING_V243 = NAMING_V241 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX,
NAMING_V245 = NAMING_V243 | NAMING_NSPAWN_LONG_HASH,
_NAMING_SCHEME_FLAGS_INVALID = -1,
} NamingSchemeFlags;

View File

@ -40,8 +40,6 @@ libudev_core_sources = '''
udev-builtin-usb_id.c
net/link-config.c
net/link-config.h
net/naming-scheme.c
net/naming-scheme.h
'''.split()
if conf.get('HAVE_KMOD') == 1

View File

@ -16,7 +16,7 @@
#include "link-config.h"
#include "log.h"
#include "memory-util.h"
#include "naming-scheme.h"
#include "netif-naming-scheme.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "parse-util.h"

View File

@ -27,7 +27,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "naming-scheme.h"
#include "netif-naming-scheme.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "stdio-util.h"

View File

@ -125,6 +125,7 @@ FastOpenNoCookie=
Source=
Metric=
TTLPropagate=
MultiPathRoute=
[Network]
IPv6DuplicateAddressDetection=
IPMasquerade=

View File

@ -12,6 +12,10 @@ IPv4LLRoute=yes
Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
Scope=link
[Route]
Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
Scope=link
[Route]
Destination=::/0
Gateway=2001:1234:5:8fff:ff:ff:ff:ff
@ -62,3 +66,18 @@ Destination=149.10.123.3
[Route]
Type=multicast
Destination=149.10.123.4
[Route]
Destination=192.168.10.1/32
MultiPathRoute=149.10.124.59@dummy98 10
MultiPathRoute=149.10.124.60@dummy98 5
[Route]
Destination=2001:1234:5:7fff:ff:ff:ff:ff/128
MultiPathRoute=2001:1234:5:8fff:ff:ff:ff:ff@dummy98 10
MultiPathRoute=2001:1234:5:9fff:ff:ff:ff:ff@dummy98 5
[Route]
Destination=192.168.10.2/32
MultiPathRoute=2001:1234:5:8fff:ff:ff:ff:ff@dummy98 10
MultiPathRoute=2001:1234:5:9fff:ff:ff:ff:ff@dummy98 5

View File

@ -1812,6 +1812,30 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output)
self.assertRegex(output, 'prohibit 202.54.1.4 proto static')
print('### ip route show 192.168.10.1')
output = check_output('ip route show 192.168.10.1')
print(output)
self.assertRegex(output, '192.168.10.1 proto static')
self.assertRegex(output, 'nexthop via 149.10.124.59 dev dummy98 weight 10')
self.assertRegex(output, 'nexthop via 149.10.124.60 dev dummy98 weight 5')
print('### ip route show 192.168.10.2')
output = check_output('ip route show 192.168.10.2')
print(output)
# old ip command does not show IPv6 gateways...
self.assertRegex(output, '192.168.10.2 proto static')
self.assertRegex(output, 'nexthop')
self.assertRegex(output, 'dev dummy98 weight 10')
self.assertRegex(output, 'dev dummy98 weight 5')
print('### ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
output = check_output('ip -6 route show 2001:1234:5:7fff:ff:ff:ff:ff')
print(output)
# old ip command does not show 'nexthop' keyword and weight...
self.assertRegex(output, '2001:1234:5:7fff:ff:ff:ff:ff')
self.assertRegex(output, 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98')
self.assertRegex(output, 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98')
def test_gateway_reconfigure(self):
copy_unit_to_networkd_unit_path('25-gateway-static.network', '12-dummy.netdev')
start_networkd()