Compare commits

...

10 Commits

Author SHA1 Message Date
Tobias Bernard be3e4467d4
Merge pull request #14333 from poettering/markdown-header-fixes
docs: make sure there's only one # markdown header in each file
2019-12-13 12:01:59 +01:00
Lennart Poettering ff2c2d0850 docs: make sure there's only one # markdown header in each file
@bertob wants us to be strict here, and only have one "#" header per
markdown file, and use "##" (or "###", …) for all others. Interestingly,
we mostly got this right already, but this fixes a few cases where this
wasn't correct.
2019-12-13 11:56:08 +01:00
Lennart Poettering db8728a60c blockdev-util: rework get_block_device()
Let's open the specified path once, and use the same fd for all lookups.
Also, don't check for btrfs twice.

The behaviour remains unmodified.
2019-12-13 18:38:35 +09:00
Yu Watanabe 2e048b14eb
Merge pull request #13915 from ddstreet/ipv6_mtu
Set ipv6 mtu after link-up or device mtu change
2019-12-13 18:33:04 +09:00
Dan Streetman 7db0544705 test-network: add tests to verify IPv6MTUBytes 2019-12-05 17:53:10 -05:00
Dan Streetman 3e82152543 test-network: disable restart limiting for networkd
Some of the tests restart networkd rapidly, so restart limiting
must be disabled to avoid start ratelimiting from causing test
failures.
2019-12-05 17:53:10 -05:00
Dan Streetman fd372b1a68 test-network: in wait_online() allow a few seconds to reach setup_state 2019-12-05 17:53:03 -05:00
Dan Streetman befd4b8b60 test-network: read link attribute at any depth 2019-12-05 17:50:15 -05:00
Dan Streetman 9dfc1a9339 test-network: allow specifying only individual drop-in files 2019-12-05 17:50:10 -05:00
Dan Streetman d236718c16 network: set ipv6 mtu after link-up or device mtu change
The kernel resets the ipv6 mtu after NETDEV_UP or NETDEV_CHANGEMTU event,
so we must reset the ipv6 mtu to our configured value after we detect
IFF_UP flag set or after we set the device mtu.

Fixes: #13914.
2019-12-05 17:49:47 -05:00
13 changed files with 238 additions and 40 deletions

View File

@ -56,7 +56,7 @@ components:
script can optionally create boot loader entries that carry an initial boot script can optionally create boot loader entries that carry an initial boot
counter (the initial counter is configurable in `/etc/kernel/tries`). counter (the initial counter is configurable in `/etc/kernel/tries`).
# Details ## Details
The boot counting data `systemd-boot` and `systemd-bless-boot.service` The boot counting data `systemd-boot` and `systemd-bless-boot.service`
manage is stored in the name of the boot loader entries. If a boot loader entry manage is stored in the name of the boot loader entries. If a boot loader entry
@ -149,7 +149,7 @@ scenario the first 4 steps are the same as above:
12. On the following boot (and all subsequent boots after that) the entry is 12. On the following boot (and all subsequent boots after that) the entry is
now seen with boot counting turned off, no further renaming takes place. now seen with boot counting turned off, no further renaming takes place.
# How to adapt this scheme to other setups ## How to adapt this scheme to other setups
Of the stack described above many components may be replaced or augmented. Here Of the stack described above many components may be replaced or augmented. Here
are a couple of recommendations. are a couple of recommendations.
@ -180,7 +180,7 @@ are a couple of recommendations.
wrap them in a unit and order them after `boot-complete.target`, pulling it wrap them in a unit and order them after `boot-complete.target`, pulling it
in. in.
# FAQ ## FAQ
1. *Why do you use file renames to store the counter? Why not a regular file?* 1. *Why do you use file renames to store the counter? Why not a regular file?*
— Mainly two reasons: it's relatively likely that renames can be implemented — Mainly two reasons: it's relatively likely that renames can be implemented

View File

@ -543,7 +543,7 @@ layout: default
time you need that please immediately undefine `basename()`, and add a time you need that please immediately undefine `basename()`, and add a
comment about it, so that no code ever ends up using the POSIX version! comment about it, so that no code ever ends up using the POSIX version!
# Committing to git ## Committing to git
- Commit message subject lines should be prefixed with an appropriate component - Commit message subject lines should be prefixed with an appropriate component
name of some kind. For example "journal: ", "nspawn: " and so on. name of some kind. For example "journal: ", "nspawn: " and so on.

View File

@ -69,7 +69,7 @@ Once you're done, create a git commit for the update of the `po/*.po` file you
touched. Remember to undo the changes to the other `*.po` files (for instance, touched. Remember to undo the changes to the other `*.po` files (for instance,
using `git checkout -- po/` after you commit the changes you do want to keep.) using `git checkout -- po/` after you commit the changes you do want to keep.)
# Recompiling Translations ## Recompiling Translations
You can recompile the `*.po` files using the following command: You can recompile the `*.po` files using the following command:

View File

@ -54,32 +54,36 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
return 1; return 1;
} }
int get_block_device(const char *path, dev_t *dev) { int get_block_device(const char *path, dev_t *ret) {
_cleanup_close_ int fd = -1;
struct stat st; struct stat st;
struct statfs sfs; int r;
assert(path); assert(path);
assert(dev); assert(ret);
/* Gets the block device directly backing a file system. If /* Gets the block device directly backing a file system. If the block device is encrypted, returns
* the block device is encrypted, returns the device mapper * the device mapper block device. */
* block device. */
if (lstat(path, &st)) fd = open(path, O_NOFOLLOW|O_CLOEXEC);
if (fd < 0)
return -errno;
if (fstat(fd, &st))
return -errno; return -errno;
if (major(st.st_dev) != 0) { if (major(st.st_dev) != 0) {
*dev = st.st_dev; *ret = st.st_dev;
return 1; return 1;
} }
if (statfs(path, &sfs) < 0) r = btrfs_get_block_device_fd(fd, ret);
return -errno; if (r > 0)
return 1;
if (r != -ENOTTY) /* not btrfs */
return r;
if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) *ret = 0;
return btrfs_get_block_device(path, dev);
*dev = 0;
return 0; return 0;
} }

View File

@ -1350,7 +1350,7 @@ int link_set_mtu(Link *link, uint32_t mtu) {
if (link_ipv6_enabled(link) && mtu < IPV6_MIN_MTU) { if (link_ipv6_enabled(link) && mtu < IPV6_MIN_MTU) {
log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as "
"IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes");
mtu = IPV6_MIN_MTU; mtu = IPV6_MIN_MTU;
} }
@ -2387,9 +2387,23 @@ static int link_set_ipv6_mtu(Link *link) {
if (link->network->ipv6_mtu == 0) if (link->network->ipv6_mtu == 0)
return 0; return 0;
/* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes
* on the interface. Bump up IPv6 MTU bytes to IPV6_MTU_MIN. */
if (link->network->ipv6_mtu < IPV6_MIN_MTU) {
log_link_notice(link, "Bumping IPv6 MTU to "STRINGIFY(IPV6_MIN_MTU)" byte minimum required");
link->network->ipv6_mtu = IPV6_MIN_MTU;
}
r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu); r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu);
if (r < 0) if (r < 0) {
log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m"); if (link->mtu < link->network->ipv6_mtu)
log_link_warning(link, "Cannot set IPv6 MTU %"PRIu32" higher than device MTU %"PRIu32,
link->network->ipv6_mtu, link->mtu);
else
log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m");
}
link->ipv6_mtu_set = true;
return 0; return 0;
} }
@ -2696,10 +2710,6 @@ static int link_configure(Link *link) {
if (r < 0) if (r < 0)
return r; return r;
r = link_set_ipv6_mtu(link);
if (r < 0)
return r;
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) { if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) {
r = ipv4ll_configure(link); r = ipv4ll_configure(link);
if (r < 0) if (r < 0)
@ -2776,6 +2786,12 @@ static int link_configure_after_setting_mtu(Link *link) {
if (link->setting_mtu) if (link->setting_mtu)
return 0; return 0;
/* The kernel resets ipv6 mtu after changing device mtu;
* we must set this here, after we've set device mtu */
r = link_set_ipv6_mtu(link);
if (r < 0)
return r;
if (link_has_carrier(link) || link->network->configure_without_carrier) { if (link_has_carrier(link) || link->network->configure_without_carrier) {
r = link_acquire_conf(link); r = link_acquire_conf(link);
if (r < 0) if (r < 0)
@ -3484,11 +3500,30 @@ int link_carrier_reset(Link *link) {
return 0; return 0;
} }
/* This is called every time an interface admin state changes to up;
* specifically, when IFF_UP flag changes from unset to set */
static int link_admin_state_up(Link *link) {
int r;
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. The check for
* ipv6_mtu_set prevents this from trying to set it too early before
* the link->network has been setup; we only need to reset it
* here if we've already set it during normal initialization. */
if (link->ipv6_mtu_set) {
r = link_set_ipv6_mtu(link);
if (r < 0)
return r;
}
return 0;
}
int link_update(Link *link, sd_netlink_message *m) { int link_update(Link *link, sd_netlink_message *m) {
struct ether_addr mac; struct ether_addr mac;
const char *ifname; const char *ifname;
uint32_t mtu; uint32_t mtu;
bool had_carrier, carrier_gained, carrier_lost; bool had_carrier, carrier_gained, carrier_lost, link_was_admin_up;
int old_master, r; int old_master, r;
assert(link); assert(link);
@ -3618,12 +3653,22 @@ int link_update(Link *link, sd_netlink_message *m) {
old_master = link->master_ifindex; old_master = link->master_ifindex;
(void) sd_netlink_message_read_u32(m, IFLA_MASTER, (uint32_t *) &link->master_ifindex); (void) sd_netlink_message_read_u32(m, IFLA_MASTER, (uint32_t *) &link->master_ifindex);
link_was_admin_up = link->flags & IFF_UP;
had_carrier = link_has_carrier(link); had_carrier = link_has_carrier(link);
r = link_update_flags(link, m, old_master != link->master_ifindex); r = link_update_flags(link, m, old_master != link->master_ifindex);
if (r < 0) if (r < 0)
return r; return r;
if (!link_was_admin_up && (link->flags & IFF_UP)) {
log_link_info(link, "Link UP");
r = link_admin_state_up(link);
if (r < 0)
return r;
} else if (link_was_admin_up && !(link->flags & IFF_UP))
log_link_info(link, "Link DOWN");
r = link_update_lldp(link); r = link_update_lldp(link);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -116,6 +116,7 @@ typedef struct Link {
bool routing_policy_rules_configured:1; bool routing_policy_rules_configured:1;
bool qdiscs_configured:1; bool qdiscs_configured:1;
bool setting_mtu:1; bool setting_mtu:1;
bool ipv6_mtu_set:1;
LIST_HEAD(Address, pool_addresses); LIST_HEAD(Address, pool_addresses);

View File

@ -0,0 +1,5 @@
[Match]
OriginalName=dummy98
[Link]
MTUBytes=1600

View File

@ -0,0 +1,4 @@
[NetDev]
Name=dummy98
Kind=dummy
MTUBytes=1600

View File

@ -0,0 +1,7 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
Address=2001:db8:0:f101::15/64

View File

@ -0,0 +1,2 @@
[Network]
IPv6MTUBytes=1400

View File

@ -0,0 +1,2 @@
[Network]
IPv6MTUBytes=1550

View File

@ -0,0 +1,2 @@
[Link]
MTUBytes=1600

View File

@ -142,6 +142,8 @@ def setUpModule():
running_units.append(u) running_units.append(u)
drop_in = [ drop_in = [
'[Unit]',
'StartLimitIntervalSec=0',
'[Service]', '[Service]',
'Restart=no', 'Restart=no',
'ExecStart=', 'ExecStart=',
@ -216,8 +218,8 @@ def tearDownModule():
for u in running_units: for u in running_units:
check_output(f'systemctl start {u}') check_output(f'systemctl start {u}')
def read_link_attr(link, dev, attribute): def read_link_attr(*args):
with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f: with open(os.path.join('/sys/class/net/', *args)) as f:
return f.readline().strip() return f.readline().strip()
def read_bridge_port_attr(bridge, link, attribute): def read_bridge_port_attr(bridge, link, attribute):
@ -267,14 +269,33 @@ def read_ipv4_sysctl_attr(link, attribute):
with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f: with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f:
return f.readline().strip() return f.readline().strip()
def copy_unit_to_networkd_unit_path(*units): def copy_unit_to_networkd_unit_path(*units, dropins=True):
"""Copy networkd unit files into the testbed.
Any networkd unit file type can be specified, as well as drop-in files.
By default, all drop-ins for a specified unit file are copied in;
to avoid that specify dropins=False.
When a drop-in file is specified, its unit file is also copied in automatically.
"""
print() print()
for unit in units: for unit in units:
shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path) if dropins and os.path.exists(os.path.join(networkd_ci_path, unit + '.d')):
if (os.path.exists(os.path.join(networkd_ci_path, unit + '.d'))):
copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d')) copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d'))
if unit.endswith('.conf'):
dropin = unit
dropindir = os.path.join(network_unit_file_path, os.path.dirname(dropin))
os.makedirs(dropindir, exist_ok=True)
shutil.copy(os.path.join(networkd_ci_path, dropin), dropindir)
unit = os.path.dirname(dropin).rstrip('.d')
shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path)
def remove_unit_from_networkd_path(units): def remove_unit_from_networkd_path(units):
"""Remove previously copied unit files from the testbed.
Drop-ins will be removed automatically.
"""
for unit in units: for unit in units:
if (os.path.exists(os.path.join(network_unit_file_path, unit))): if (os.path.exists(os.path.join(network_unit_file_path, unit))):
os.remove(os.path.join(network_unit_file_path, unit)) os.remove(os.path.join(network_unit_file_path, unit))
@ -352,7 +373,7 @@ class Utilities():
def check_operstate(self, link, expected, show_status=True, setup_state='configured'): def check_operstate(self, link, expected, show_status=True, setup_state='configured'):
self.assertRegex(get_operstate(link, show_status, setup_state), expected) self.assertRegex(get_operstate(link, show_status, setup_state), expected)
def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, setup_state='configured'): def wait_online(self, links_with_operstate, timeout='20s', bool_any=False, setup_state='configured', setup_timeout=5):
args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
if bool_any: if bool_any:
args += ['--any'] args += ['--any']
@ -363,13 +384,23 @@ class Utilities():
output = check_output(*networkctl_cmd, 'status', link.split(':')[0], env=env) output = check_output(*networkctl_cmd, 'status', link.split(':')[0], env=env)
print(output) print(output)
raise raise
if not bool_any: if not bool_any and setup_state:
for link in links_with_operstate: # check at least once now, then once per sec for setup_timeout secs
output = check_output(*networkctl_cmd, 'status', link.split(':')[0]) for secs in range(setup_timeout + 1):
print(output) for link in links_with_operstate:
for line in output.splitlines(): output = check_output(*networkctl_cmd, 'status', link.split(':')[0])
if 'State:' in line: print(output)
self.assertRegex(line, setup_state) if not re.search(rf'(?m)^\s*State:.*({setup_state}).*$', output):
# this link isn't in the right state; break into the sleep below
break
else:
# all the links were in the right state; break to exit the timer loop
break
# don't bother sleeping if time is up
if secs < setup_timeout:
time.sleep(1)
else:
self.fail(f'link {link} state does not match {setup_state}')
def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100): def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
for i in range(timeout_sec): for i in range(timeout_sec):
@ -3304,6 +3335,101 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertRegex(output, '2001:db8:0:1::/64 proto ra') self.assertRegex(output, '2001:db8:0:1::/64 proto ra')
class NetworkdMTUTests(unittest.TestCase, Utilities):
links = ['dummy98']
units = [
'12-dummy.netdev',
'12-dummy-mtu.netdev',
'12-dummy-mtu.link',
'12-dummy.network',
]
def setUp(self):
remove_links(self.links)
stop_networkd(show_logs=False)
def tearDown(self):
remove_log_file()
remove_links(self.links)
remove_unit_from_networkd_path(self.units)
stop_networkd(show_logs=True)
def check_mtu(self, mtu, ipv6_mtu=None, reset=True):
if not ipv6_mtu:
ipv6_mtu = mtu
# test normal start
start_networkd()
self.wait_online(['dummy98:routable'])
self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu)
self.assertEqual(read_link_attr('dummy98', 'mtu'), mtu)
# test normal restart
restart_networkd()
self.wait_online(['dummy98:routable'])
self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), ipv6_mtu)
self.assertEqual(read_link_attr('dummy98', 'mtu'), mtu)
if reset:
self.reset_check_mtu(mtu, ipv6_mtu)
def reset_check_mtu(self, mtu, ipv6_mtu=None):
''' test setting mtu/ipv6_mtu with interface already up '''
stop_networkd()
# note - changing the device mtu resets the ipv6 mtu
run('ip link set up mtu 1501 dev dummy98')
run('ip link set up mtu 1500 dev dummy98')
self.assertEqual(read_link_attr('dummy98', 'mtu'), '1500')
self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'mtu'), '1500')
self.check_mtu(mtu, ipv6_mtu, reset=False)
def test_mtu_network(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf')
self.check_mtu('1600')
def test_mtu_netdev(self):
copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network', dropins=False)
# note - MTU set by .netdev happens ONLY at device creation!
self.check_mtu('1600', reset=False)
def test_mtu_link(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network', dropins=False)
# must reload udev because it only picks up new files after 3 second delay
call('udevadm control --reload')
# note - MTU set by .link happens ONLY at udev processing of device 'add' uevent!
self.check_mtu('1600', reset=False)
def test_ipv6_mtu(self):
''' set ipv6 mtu without setting device mtu '''
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1400.conf')
self.check_mtu('1500', '1400')
def test_ipv6_mtu_toolarge(self):
''' try set ipv6 mtu over device mtu (it shouldn't work) '''
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
self.check_mtu('1500', '1500')
def test_mtu_network_ipv6_mtu(self):
''' set ipv6 mtu and set device mtu via network file '''
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy.network.d/mtu.conf', '12-dummy.network.d/ipv6-mtu-1550.conf')
self.check_mtu('1600', '1550')
def test_mtu_netdev_ipv6_mtu(self):
''' set ipv6 mtu and set device mtu via netdev file '''
copy_unit_to_networkd_unit_path('12-dummy-mtu.netdev', '12-dummy.network.d/ipv6-mtu-1550.conf')
self.check_mtu('1600', '1550', reset=False)
def test_mtu_link_ipv6_mtu(self):
''' set ipv6 mtu and set device mtu via link file '''
copy_unit_to_networkd_unit_path('12-dummy.netdev', '12-dummy-mtu.link', '12-dummy.network.d/ipv6-mtu-1550.conf')
# must reload udev because it only picks up new files after 3 second delay
call('udevadm control --reload')
self.check_mtu('1600', '1550', reset=False)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir') parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')