Currently IPv6 code has to provide _two_ interface pointers to nd6_output_ifp() in order to pass the packet.
They are the same interface _except_ the loopback case: when first one (which if_output() function is used) is "loX", the second one needs to be "real" network interface where source/dst IPv6 addresses are from.
The reason besides this was the following:
Quoting KAME Changelog 1.910 2000/05/21:
2000-05-17 JINMEI, Tatuya <email@example.com> * kame/sys/netinet6/nd6.c (nd6_output): * kame/sys/netinet6/ip6_output.c (ip6_output): made scoped addresses more friendly with loopback interfaces; if a packet containing scoped addresses in the source or destination address fields is going to be sent on a loopback interface, pass looutput() the interface to which the scoped address(es) belongs. For example, you won't see "lo0" when you ping to your own link-local address. This is currently experimental and enabled only when the FAKE_LOOPBACK_IF kernel compile option is specified.
Actual commit in KAME: https://github.com/kame/kame/commit/1b854ebf614ac68a25ddd067843766655efdbb8b
It was turned on by default several months after:
2000-07-30 JINMEI, Tatuya <firstname.lastname@example.org> * kame/sys/netinet6/(various files): made FAKE_LOOPBACK_IF default (as previously announced). At this moment, the older behavior can be specified by the "OLD_LOOPBACK_IF" kernel compilation option.
These changes were merged to FreeBSD as rS62587 .
There were a bunch of issues with this approach:
- BPF has to handle this explicitly (worked around in rS162539)
- loopback checksum problems (partially worked around in rS238871 , see kern/170070 )
- Strange packet accounting: kern/165190
I believe current behavior needs to be changed because:
- setting dst-ip ifp as _source_ of loopbacked packets is simply incorrect
- accounting outgoing packets/bytes to physical interface instead of loopback is also incorrect
- this makes IPv6 handling different from IPv4 for no reason
- this behavior brings unnecessary complexity to inet6/, routing and loopback code
The actual proposal is to eliminate second ("origifp" interface pointer) and use the same handling logic as in IPv4.
What will change:
- looutput() will count opackets/obytes on loopback
- mac_ifnet_check_transmit() from looutput will also be called for loopback interface
- if_simloop() will be called with loopback interface (exactly as in IPv4)
The latter might require some changes in firewalls configs, but typically loopback traffic is explicitly allowed (and now IPv6 behaviour is exactly the same as IPv4 in this place)