Skip to content
FreeBSDDeep Dive Published Updated 7 min readViews unavailable

FreeBSD ip6addrctl: Diagnose IPv4 and IPv6 Address Selection

Control FreeBSD address-selection policy with ip6addrctl, test application ordering, persist verified changes, and avoid confusing policy with routing.

When a dual-stack FreeBSD service chooses IPv4 while an IPv6 path exists, or chooses an unexpected IPv6 source address, the route table may not be the first thing to change. The kernel and resolver use address-selection policy to rank candidate source and destination addresses. ip6addrctl(8) displays and configures the policy table that FreeBSD applies to outgoing IPv4 and IPv6 traffic. It does not create addresses, repair neighbor discovery, install routes, or force every application to attempt the first address returned.

This boundary is operationally important. A policy change can influence many programs on the host, but it cannot make an unreachable address reachable. Start with the current table and the application’s actual destination list, then correlate the chosen pair with routing and packet evidence. Test one policy adjustment at a time and make it persistent only after a controlled functional check.

Separate the selection layers

Address selection happens across distinct layers. Name service can produce a set of destination addresses. An application may reorder, filter, race, or choose from that set according to its own behavior. The host’s address-selection policy influences source and destination ranking, while routing determines the next hop and outgoing interface for the selected destination. Neighbor discovery, firewall policy, and remote service configuration then determine whether packets can complete a connection.

Therefore, three observations are more useful than a single ping result:

  1. What addresses did the application receive from its configured name-service path?
  2. What policy table is currently installed?
  3. Which source address, route, interface, and peer response appear during the failing connection?

Capture the release, resolver configuration, interface addresses, and current policy before changing anything:

freebsd-version -kru
ip6addrctl show
ifconfig -a
netstat -rn -f inet
netstat -rn -f inet6
getent hosts service.example.net

Use a real service name. The documentation name service.example.net is an example only. getent exercises the configured name-service lookup path; it does not prove which address a particular application eventually uses. Some programs use their own resolver, cache answers, or implement connection racing. Capture the failing program’s own behavior when that distinction matters.

Read the policy table as precedence and labels

Each installed row contains an IPv6 prefix, a decimal precedence value, and a label. IPv4 policy entries are represented with IPv4-mapped IPv6 prefixes. The table participates in address-selection rules described by RFC 6724. It is not a list of interface routes, DNS server priorities, or firewall rules. A row can change the relative ranking of candidates without making an address available on the host.

Display the active policy rather than assuming a configuration file was applied:

ip6addrctl show

If the host is using a jail, the utility supports an explicit jail selector:

ip6addrctl -j appjail show

The -j option belongs before the operation. A jail-specific view helps distinguish policy inside the jail from the host’s policy. The jail’s address availability and route setup remain separate questions.

Avoid copying a precedence table from another operating system without understanding its prefixes and intended outcome. A wrong table can prefer a destination family that is present in DNS but unusable from this network. Labels also participate in matching source and destination address properties, so editing one number without modeling the candidate addresses can produce surprising results.

Build a safe experiment

Use a disposable test host or a maintenance window with out-of-band access if the policy affects a remote management path. Save the exact output of ip6addrctl show, the current configuration file, and the intended rollback. Do not begin with flush: the manual defines it as deleting all existing policy entries in the kernel. A partial or empty policy can change selection for unrelated processes.

For a temporary, narrowly scoped policy change, use the documented add and delete operations only after verifying the exact prefix and current table:

# Illustrative syntax only; select values from the approved policy design.
ip6addrctl add ::ffff:192.0.2.0/120 35 4
ip6addrctl show

The example row uses a documentation-only IPv4-mapped prefix and arbitrary decimal values. It is not a recommended production preference. Choose values based on the intended policy and RFC semantics, and verify that the prefix is not already installed because add expects a new entry. To undo the exact temporary row, use delete with the same prefix and confirm the resulting table:

ip6addrctl delete ::ffff:192.0.2.0/120
ip6addrctl show

Do not treat a successful command exit status as proof of a correct application outcome. Re-run the specific name resolution and connection test, capture the selected source address and interface, and compare both address families. If an application still fails, determine whether it consumed the changed policy or uses a different resolver or connection strategy.

Persist only an understood policy

FreeBSD’s startup configuration exposes ip6addrctl_enable and ip6addrctl_policy. In the automatic policy mode, the startup logic can read /etc/ip6addrctl.conf; verify the exact defaults and behavior against the installed rc.conf(5) and ip6addrctl(8) for the target release. Do not edit /etc/defaults/rc.conf. Keep local policy in the documented local configuration file and manage it through the host’s normal configuration-control process.

An illustrative custom table file has one prefix, precedence, and label per line:

# Example format only; the values are not a universal recommendation.
::ffff:0:0/96 35 4
2001:db8::/32 5 5

2001:db8::/32 is reserved for documentation and must be replaced with a real prefix only as part of a reviewed policy. The IPv4-mapped entry above demonstrates the required representation. Validate that every line follows the documented format and that comments are intentional. Install a reviewed file explicitly in a test environment:

ip6addrctl install /etc/ip6addrctl.conf
ip6addrctl show

The manual notes that a policy file is processed before attaching to a jail. That timing matters when a jail is created from startup scripts. If the active table differs from the desired file after boot, investigate rc ordering, variable values, and jail lifecycle rather than repeatedly applying a shell command and leaving persistence unresolved.

Distinguish policy failures from route and path failures

If the name query returns only an IPv4 address, changing IPv6 precedence cannot add an AAAA record. If the host has no suitable IPv6 source address, selection policy cannot invent one. If the IPv6 route points to the wrong next hop, change routing through the reviewed route configuration. If Neighbor Discovery fails, inspect neighbor state and packet capture. If packets leave but no response returns, investigate the remote route, firewall, service listener, and return path.

The inverse is also possible: a service has valid IPv4 and IPv6 records, but the first ranked address is not reachable from a specific VLAN or jail. A different client implementation may retry another address while a single-address test does not. Compare a direct family-specific connection with the application’s normal connection path. Record latency and failure behavior for both families rather than optimizing only for a successful TCP handshake.

Avoid broad changes such as preferring IPv4 globally because one remote endpoint has a broken IPv6 route. That may hide the real defect and regress other services. If only one application or destination needs special handling, use an application-level endpoint policy or fix the destination’s published records and network path where those are under your control.

Acceptance criteria

A policy change is ready when the operator can show the old and new kernel table, explain the matched prefix and its precedence/label effect, and demonstrate the intended behavior with the actual application. Verify after reboot or jail recreation that the persistent configuration installed the expected entries. Keep a rollback path and an independent management address available.

The record should include release, policy file, relevant addresses, name-service result, selected source/destination pair, route, interface, and test timestamps. A policy table is one input to endpoint selection, not an availability mechanism. Keeping that distinction explicit prevents a ranking adjustment from being mistaken for a network repair.

Related:

Sources:

Comments