Problem statement
Currently for the loopback routes addition requests with link-level gateway, source ifa selection (rt_ifa) is really hackish. The route goes through the entire insertion process with invalid ifa and got it changed immediately after insertion. This leads to complications in the various parts of routing stack.
Let's trace addition of the loopback interfaces route:
ifa_add_loopback_route() # is a wrapper for ifa_maintain_loopback_route() # fills in _info_ structure with interface set to loopback but not filling in ifa. rtequest1_fib() # validates and fills in remaining data, determes ifa by calling rt_getifa_fib() # which uses gw & interface to call ifaof_ifpforaddr() # which returns the link-level ifa matching link-level gw (AF_LINK "base" ifa for lo0)
Then, the route gets inserted into the routing table with invalid rt_ifa, we drop RIB_WLOCK and call ifa routing hook.
This hook is
link_rtrequest() # which again calls ifaof_ifpforaddr() # this time with the `dst` as the socket, changes ifa to be the "proper" one and calls that rta handler.
This behaviour complicates routing code:
- at the moment of insertion ifa pointer is plain wrong
- ifa selection is way more complex
Proposed solution
First, setup ifa explicitly for the loopback routes (always loopback IPv4/IPv6 address). This is effectively what we do now for both IPv4/IPv6 (see below).
Note: for IPv6 route IFA plays less important role as it uses its own SAS process, which selects the proper address by its own.
Second, move the logic from the link_rtrequest() to the rt_ifa_ifp(). High-level description: if the gateway is set, use gateway, otherwise use dst to guess ifa. However, still retain existing behaviour to ensure we still have _some_ ifa.
For example, it can be helpful if one adds directly-reachable interface prefix to an interface without any IP address.
Side effects
Userland applications do not currently receive and RTM_ADD notifications for the loopback route. Though, they scn routing table periodically, so the returned loopback route data matters. The test section addresses verification of the (lack of change) .
The only kernel applications subscribed for such notifications are IPv6 and SCTP handlers. Both don't care about the IFA in that particular case.
Testing
Combination of 'route -n monitor' output for the 'route -n get ....' are shown below as stock route get / netstat -rn does not show ifa.
Before
m@devel0 route -n get -6 2a01:4f8:13a:70c:ffff::6
sockaddrs: <DST,GATEWAY,IFP,IFA>
2a01:4f8:13a:70c:ffff::6 link#1 lo0 ::1
m@devel0 route -n get -6 fe80::5054:ff:fe42:fef%vtnet0
sockaddrs: <DST,GATEWAY,IFP,IFA>
fe80::5054:ff:fe42:fef%vtnet0 link#1 lo0 ::1
14:48 [2] m@devel0 route -n get -6 ::1
sockaddrs: <DST,GATEWAY,IFP,IFA>
::1 link#2 lo0 ::1
19:15 [1] m@devel2 route -n get -6 fe80::1%lo0
sockaddrs: <DST,GATEWAY,IFP,IFA>
fe80::1%lo0 link#2 lo0 fe80::1%lo0
After
route -n get 10.0.0.157
sockaddrs: <DST,GATEWAY,IFP,IFA>
10.0.0.157 link#1 lo0 127.0.0.1
route -n get -6 ::ffff:0.0.0.0
sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA>
::ffff:0.0.0.0 ::1 ffff:ffff:ffff:ffff:ffff:ffff:: lo0 ::1
route -n get -6 2a01:4f8:13a:70c:ffff::6
sockaddrs: <DST,GATEWAY,IFP,IFA>
2a01:4f8:13a:70c:ffff::6 link#1 lo0 ::1
route -n get -6 fe80::5054:ff:fe42:fef%vtnet0
sockaddrs: <DST,GATEWAY,IFP,IFA>
fe80::5054:ff:fe42:fef%vtnet0 link#1 lo0 ::1
route -n get -6 fe80::1%lo0
sockaddrs: <DST,GATEWAY,IFP,IFA>
fe80::1%lo0 link#2 lo0 fe80::1%lo0