Bug #17039
openNAT64 fails when PPPoE interface address is also configured as a CARP VIP on another interface
0%
Description
There are two related issues involved.
pfSense has an existing address-discovery problem when an IPv4 address is simultaneously the actual address of a PPPoE interface and a CARP VIP on another interface. This behavior already existed in 2.8.1 and affected other functionality, but NAT64 continued to work in 2.8.1.
In 2.9.0, NAT64 now fails because its source-address resolution encounters this existing problem.
The underlying PPPoE/VIP address-discovery problem therefore predates 2.9.0, while the NAT64 failure is a 2.9.0 regression.
ENVIRONMENT¶
pfSense CE 2.9.0-RELEASE
amd64
FreeBSD 16.0-CURRENT
Build: RELENG_2_9_0-n256132-d8e3138ecf52
NETWORK TOPOLOGY¶
The ISP provisions the connection as follows.- The Internet connection terminates as a PPPoE session on pppoe0.
- pppoe0 has IPv4 address 11.22.33.65/32.
- The ISP routes 11.22.33.64/28 through the PPPoE session.
- There is no separate WAN interface for this routed /28.
- OPT2 carries the routed /28 internally.
- OPT2 has primary address 192.168.67.1.
- 11.22.33.65 is configured as a CARP VIP on OPT2.
Other hosts on the routed network use addresses such as 11.22.33.66, 11.22.33.67, etc.
Therefore 11.22.33.65 legitimately exists in two contexts:- pppoe0: 11.22.33.65/32, actual PPPoE interface address
- OPT2: 192.168.67.1 primary address, with 11.22.33.65 as a CARP VIP
This is intentional and required by the ISP's routing model.
EXISTING ADDRESS-RESOLUTION PROBLEM¶
The PPPoE address is correctly returned by the lower-level interface-address API.
Command:php -r 'require_once("config.inc"); require_once("interfaces.inc"); var_dump(pfSense_get_ifaddrs("pppoe0"));'
The result includes:["addrs"] =>
array(1) {
[0] =>
array(4) {
["addr"] => string(13) "11.22.33.65"
["subnetbits"] => int(32)
["subnet"] => string(15) "255.255.255.255"
["dstaddr"] => string(13) "78.33.253.139"
}
}
However:
php -r 'require_once("config.inc"); require_once("interfaces.inc"); var_dump(get_interface_addresses("pppoe0"));'
does not return an ipaddr field.
Consequently:php -r 'require_once("config.inc"); require_once("interfaces.inc"); var_dump(get_interface_ip("opt5"));'
returns:
NULL
CAUSE¶
get_interface_addresses() builds a global list of configured IPv4 VIP addresses.
The relevant code is:$v4vips = array();
foreach (array_keys(get_configured_vip_list()) as $viface) {
$vip = get_configured_vip($viface);
if (is_ipaddrv4($vip['subnet'])) {
array_push($v4vips, $vip['subnet']);
}
}
It then filters the addresses returned for the interface:$v4addrs = array_filter($ifaddrs['addrs'], function($addr) use ($v4vips){
return (array_search($addr['addr'], $v4vips) === false);
});
The configured VIP list contains 11.22.3365 because it is a CARP VIP on OPT2.
When processing pppoe0, the same address is therefore removed from the list of actual addresses belonging to pppoe0.
The filtering does not appear to distinguish which interface owns the VIP.
As a result:
pfSense_get_ifaddrs("pppoe0") correctly finds 11.22.33.65.
get_interface_addresses("pppoe0") filters it out.
get_interface_ip("opt5") returns NULL.
VERSION COMPARISON¶
pfSense 2.8.1:
The underlying PPPoE/VIP address-resolution problem already existed.
For this configuration, get_interface_ip("opt5") could fail to resolve the PPPoE address, affecting other pfSense functionality that relied on the common interface-address API.
However, NAT64 still worked in 2.8.1.
- After upgrading to 2.9.0, NAT64 no longer works.
- The NAT64 rule remains present in the GUI but shows zero hits.
- The generated rules contain messages such as:
- Could not determine NAT64 source address - Allow NAT64 traffic
- There is no corresponding NAT64 translation rule in pfctl.
- The affected NAT rule uses opt5ip as its source.
- Changing the rule to use an address/interface that does not encounter this address-resolution problem causes the NAT64 rule to be generated and traffic counters to increase.
IMPACT¶
The immediate regression in 2.9.0 is that NAT64 no longer works when using the PPPoE interface address as the NAT source.
The underlying address-resolution problem can also affect other pfSense functionality using get_interface_ip().
This does not appear to be a NAT64 configuration problem. The lower-level interface-address API correctly discovers the PPPoE address; the address is subsequently discarded by get_interface_addresses() because it also exists as a VIP on another interface.
EXPECTED BEHAVIOR¶
When obtaining the addresses of an interface, a VIP configured on another interface should not cause an identical address that is actually assigned to the queried interface to be discarded.
Specifically:
get_interface_addresses("pppoe0")
should return an ipaddr of:
11.22.33.65
and:
get_interface_ip("opt5")
should return:
11.22.33.65
The presence of 11.22.33.65 as a CARP VIP on OPT2 should not prevent it from being recognized as the actual address of pppoe0.
ACTUAL BEHAVIOR¶
pfSense_get_ifaddrs("pppoe0")
11.22.33.65
get_interface_addresses("pppoe0")
no ipaddr
get_interface_ip("opt5")
NULL
NAT64 in 2.9.0
cannot determine source address
NAT64 translation rule is not generated
SUGGESTED FIX¶
The VIP filtering in get_interface_addresses() should be made interface-aware.
An address should not be discarded merely because the same address exists somewhere in the global VIP list. In particular, an address that is actually assigned to the interface being queried must remain available as that interface's address.
The fix should preserve the existing behavior of excluding VIP addresses where appropriate, while avoiding the removal of an address that is simultaneously a legitimate address of another interface.
ST Updated by SERGIO TALARICO 23 days ago
I hit what looks like a related but more severe manifestation of this address-resolution problem on 2.9.0.
Setup: Dell R440, Intel X710-T4L (ixl driver). Two PPPoE WANs from different ISPs, both tagged VLAN 835 (ixl0.835 and ixl1.835). LAN on a LACP lagg (lagg0 = ixl2 + ixl3). IPv4 VIP (IP Alias, public /29) on the LAN interface. Both ISPs use 192.168.100.1 as the PPP peer address, which is also the LAN interface address.
Symptom on 2.9.0: neither PPPoE session obtains an IPv4 address. PPP negotiation completes normally (LCP, PAP auth, IPCP), the ISP assigns a valid public IP, then address assignment fails and the session is torn down in a loop. Total WAN outage.
[wan] IPCP: state change Ack-Sent --> Opened
[wan] IPCP: LayerUp
[wan] 80.180.101.111 -> 192.168.100.1
[wan] IFACE: Adding IPv4 address to pppoe1 failed(IGNORING for now...): File exists
[wan] IFACE: Removing IPv4 address from pppoe1 failed(...): Can't assign requested address
With the if_pppoe kernel backend the kernel logs if_pppoe: pppoe1: failed to set IP address 17 and failed to set default route 2.
The assigned address never appears on any interface. Manual assignment also fails:
ifconfig pppoe1 inet 80.180.101.111 192.168.100.1 netmask 255.255.255.255 → SIOCAIFADDR: File exists
Additionally, at boot pfSense spawns two mpd5 instances per PPP config file (two for mpd_wan.conf, two for mpd_opt1.conf), both using the same pid file.
Versions tested with the identical config:
2.9.0: fails
2.7.2 (clean ISO install): fails the same way
2.8.1: works normally, including with the LAN address overlapping the PPP peer address
A tcpdump on the tagged VLAN confirms the ISP side is fine: PADI/PADO/PADR/PADS, LCP, PAP ACK and IPCP all complete, and pfSense is the side sending the Terminate Request after the interface address failure.
Downgrading to 2.8.1 resolved it completely with no configuration change. Happy to provide full logs or test patches.