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:

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/<ifname>/: 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'

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
  version: 2
  renderer: networkd
      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.