1. Introduction
Although networking is important for many systems, not all network protocols are supported by all devices. Still, user software or even the operating system (OS) might decide to use a newer protocol to communicate with an older or non-supported device. For example, a machine only configured to use Internet Protocol (IP) version 4 (IPv4) wouldn’t be able to receive data via IPv6. In this case, we can encounter unexpected behavior, crashes, and other problems.
In this tutorial, we check ways to disable the automatic configuration of IPv6. First, we talk about IPv6 autoconfiguration in general. Next, we cover the main way to change settings related to IPv6. Finally, we turn to more high-level network configuration abstractions for our needs.
We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments unless otherwise specified.
2. IPv6 Autoconfiguration
Autoconfiguration sets all necessary configurations and can produce both a global address for communication within the subnet and link-local address for intra-link transfers.
2.1. Getting an Address
Of course, we can always set a static address manually. However, to acquire an address automatically, IPv6 can use several approaches:
- stateful: centralized management, practically similar to Dynamic Host Configuration Protocol (DHCP)
- stateless: distributed self-management
Either way, IPv6 determines which information should be generated, which should be obtained, and how.
Finally, the protocol also verifies that the resulting addresses are unique.
2.2. Problems
Although the IPv6 autoconfiguration process is highly-optimized, issues can result from its use:
- unavailable DHCPv6 servers
- clashing manual and stateful IP addresses
- unsupported IPv6
While adding DHCPv6 servers can be straightforward, configuration mismanagement is usually critical. On that point, we’re going to explore how to prevent autoconfiguration from kicking in.
3. Using /proc and sysctl
Since IPv6 support is built into the Linux kernel, changes to the protocol behavior are kernel parameters. As such, we can modify the configuration via sysctl or directly in the /proc pseudo-filesystem.
3.1. IPv6 Settings
First, let’s briefly go over the settings that the kernel provides in /proc for IPv6:
- /proc/sys/net/ipv6/: top-level directory with all related data
- /proc/sys/net/ipv6/conf/: contains subdirectories with configurations for each network interface by name
- /proc/sys/net/ipv6/conf/
/ : directory with configurations for interface ifname - /proc/sys/net/ipv6/conf/all/: directory with common configurations
- /proc/sys/net/ipv6/conf/default/: directory with default configurations
In particular, we can change the same settings in each of the last three paths:
$ ls /proc/sys/net/ipv6/conf/<ifname|all|default>/
accept_dad max_addresses
accept_ra max_desync_factor
accept_ra_defrtr mc_forwarding
accept_ra_from_local mldv1_unsolicited_report_interval
accept_ra_min_hop_limit mldv2_unsolicited_report_interval
accept_ra_mtu mtu
accept_ra_pinfo ndisc_notify
accept_ra_rt_info_max_plen ndisc_tclass
accept_ra_rt_info_min_plen optimistic_dad
accept_ra_rtr_pref proxy_ndp
accept_redirects regen_max_retry
accept_source_route router_probe_interval
addr_gen_mode router_solicitation_delay
autoconf router_solicitation_interval
dad_transmits router_solicitation_max_interval
disable_ipv6 router_solicitations
disable_policy rpl_seg_enabled
drop_unicast_in_l2_multicast seg6_enabled
drop_unsolicited_na seg6_require_hmac
enhanced_dad stable_secret
force_mld_version suppress_frag_ndisc
force_tllao temp_prefered_lft
forwarding temp_valid_lft
hop_limit use_oif_addrs_only
ignore_routes_with_linkdown use_optimistic
keep_addr_on_down use_tempaddr
So, by appending one of the setting names that ls shows above to the interface path within /proc, we get the final path of a setting. For example, /proc/sys/net/ipv6/conf/all/forwarding controls forwarding for all interfaces.
All such paths have their sysctl equivalents as well:
$ echo '/proc/sys/net/ipv6/conf/all/forwarding' | sed 's/\/proc\/sys\///g;s/\//\./g'
net.ipv6.conf.all.forwarding
In this case, we pipe the output of echo to sed for processing:
- s/\/proc\/sys\///g [s]ubstitutes /proc/sys/ [g]lobally, thereby stripping the prefix
- s/\//\./g [s]ubstitutes / forward slashes with . periods [g]lobally, thus correcting the separator
This way, we have the option of either using sysctl or changing the settings file directly.
3.2. Disable IPv6
Naturally, we can always disable IPv6 entirely:
$ echo '1' > /proc/sys/net/ipv6/conf/<ifname|all|default>/disable_ipv6
$ sysctl net.ipv6.conf.<ifname|all|default>.disable_ipv6=1
While this should work, even when done for a single interface, we lose the protocol capability entirely. This means we wouldn’t be able to configure any IPv6 address, even manually.
3.3. Disable IPv6 Autoconfiguration
Alternatively, we can just disable IPv6 autoconfiguration via either sysctl or /proc by providing the path to the autoconf setting:
$ echo '0' > /proc/sys/net/ipv6/conf/<ifname|all|default>/autoconf
$ sysctl net.ipv6.conf.<ifname|all|default>.autoconf=0
Notably, we can apply the setting to any network interface by name (ifname), all with all, or just the default via default.
Depending on the Linux distribution, we might need to also disable the accept_ra setting to prevent router advertisements (RA):
$ echo '0' > /proc/sys/net/ipv6/conf/<ifname|all|default>/accept_ra
$ sysctl net.ipv6.conf.<ifname|all|default>.accept_ra=0
This way, we ensure that no IPv6 configuration takes place, thereby preventing IP version 6 address generation, among others. Depending on the kernel, we might only need to disable accept_ra_pinfo instead of accept_ra.
Finally, use_tempaddr is disabled by default and should remain so to avoid temporary address generation according to RFC 3041 – Privacy Extensions for Stateless Address Autoconfiguration in IPv6.
As usual, we can reenable by replacing 0 with 1 in any example above. Of course, we persist the settings with /etc/sysctl.conf.
4. Using Network Service Management
Although parameters for IPv6 are inherently part of the kernel configuration, some system services, such as network managers, can toggle them according to their own settings. One such service is systemd-networkd, the default for many newer Linux distributions.
Synchronizing IPv6 autoconfiguration settings or preventing them from changing depends on the manager and underlying configuration.
Of course, software like NetworkManager can replicate the parameters we already discussed in /proc. For instance, the [Network] section can entirely disable link-local addressing for IPv6 via LinkLocalAddressing=ipv4 and prevent router advertisements with IPv6AcceptRA=no. This way, we prevent much of the automatic configuration.
Alternatively, we can leverage the netplan renderer. It uses basic YAML files to control settings for each interface:
$ cat /etc/netplan/networkd.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
[...]
link-local: [ ]
dhcp6: no
accept-ra: no
[...]
As such, we can assign [ ] to the link-local field of the relevant interface (here, eth0) to prevent the automatic generation of a link-local IPv6 address. Similarly, we can disable dhcp6 and accept-ra by setting their values to no.
While this works for individual interfaces, we can’t apply it to the loopback interface lo. In that case, we can revert to /proc, sysctl, and our earlier methods.
In any case, the current systemd-networkd and general network manager configuration method should be synchronized with our IPv6 preferences.
5. Summary
In this article, we talked about ways to disable IPv6 autoconfiguration.
In conclusion, although the feature can be useful and saves much of the manual work associated with network configuration, there are ways to prevent a system from automatically configuring IPv6 on any of its interfaces.