* [RFC net-next 0/2] ipv4: Honor prohibit when oif specified @ 2026-10-19 3:24 liamsmith827 2026-10-19 3:24 ` [RFC net-next 1/2] " liamsmith827 2026-10-19 3:24 ` [RFC net-next 2/2] selftests: net: Test " liamsmith827 0 siblings, 2 replies; 4+ messages in thread From: liamsmith827 @ 2026-10-19 3:24 UTC (permalink / raw) To: netdev Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, liamsmith827 From: liamsmith827 <liamsmith827@proton.me> This RFC proposes honoring prohibit rules and routes when an output interface is specified. When flowi4_oif is set (SO_BINDTODEVICE, IP_PKTINFO, IP_UNICAST_IF, etc), prohibit rules and routes cause fib_lookup() to return -EACCES, but still allow the packet (and any necessary ARP) to be transmitted. This is documented as the expected behavior in the tests, but is confusing for route and rule maintainers. Android uses prohibit rules as the basis for its VPN "kill switch", with the expectation that any packet encountering such a rule will not be allowed out. Due to this interface binding exemption, apps can trivially bypass the kill switch. This is most serious on cellular networks, where a connection to an arbitrary IPv4 address can be established without requiring ARP resolution. The first patch preserves -EACCES from fib_lookup() instead of constructing an on-link route when an output interface is specified. This honors prohibit rules and routes while retaining the existing fallback for other lookup errors. The second patch enables the existing test that expects an interface-bound ping to fail. I would appreciate feedback on compatibility implications and whether this change is acceptable given the longstanding behavior. liamsmith827 (2): ipv4: Honor prohibit when oif specified selftests: net: Test prohibit when oif specified net/ipv4/route.c | 11 ++++++----- tools/testing/selftests/net/fcnal-test.sh | 7 +++---- 2 files changed, 9 insertions(+), 9 deletions(-) -- 2.47.3 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFC net-next 1/2] ipv4: Honor prohibit when oif specified 2026-10-19 3:24 [RFC net-next 0/2] ipv4: Honor prohibit when oif specified liamsmith827 @ 2026-10-19 3:24 ` liamsmith827 2026-10-25 17:13 ` Ido Schimmel 2026-10-19 3:24 ` [RFC net-next 2/2] selftests: net: Test " liamsmith827 1 sibling, 1 reply; 4+ messages in thread From: liamsmith827 @ 2026-10-19 3:24 UTC (permalink / raw) To: netdev Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, liamsmith827 From: liamsmith827 <liamsmith827@proton.me> When flowi4_oif is set (SO_BINDTODEVICE, IP_PKTINFO, IP_UNICAST_IF, etc), prohibit rules and routes cause fib_lookup() to return -EACCES, but still allow the packet (and any necessary ARP) to be transmitted. This is documented as the expected behavior in the tests, but is confusing for route and rule maintainers. Specifically, Android uses prohibit rules as the basis for its VPN "kill switch", with the expectation that any packet encountering such a rule will not be allowed out. Due to this interface binding exemption, apps can trivially bypass the kill switch. This is most serious on cellular networks, where a connection to an arbitrary IPv4 address can be established without requiring ARP resolution. Preserve -EACCES from fib_lookup() instead of constructing an on-link route when an output interface is specified. This honors prohibit rules and routes while retaining the existing fallback for other lookup errors. Signed-off-by: liamsmith827 <liamsmith827@proton.me> --- net/ipv4/route.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 595a3b7f1783..616c20299334 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2853,7 +2853,7 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, if (err) { res->fi = NULL; res->table = NULL; - if (fl4->flowi4_oif && + if (fl4->flowi4_oif && err != -EACCES && (ipv4_is_multicast(fl4->daddr) || !fl4->flowi4_l3mdev)) { /* Apparently, routing tables are wrong. Assume, * that the destination is on link. @@ -2864,13 +2864,14 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, * addresses. When oif is specified, routing * tables are looked up with only one purpose: * to catch if destination is gatewayed, rather than - * direct. Moreover, if MSG_DONTROUTE is set, - * we send packet, ignoring both routing tables - * and ifaddr state. --ANK - * + * direct. * * We could make it even if oif is unknown, * likely IPv6, but we do not. + * + * Additionally, when oif is specified, routing lookup + * allows us to check if destination is prohibited + * (-EACCES). LS. */ if (fl4->saddr == 0) -- 2.47.3 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [RFC net-next 1/2] ipv4: Honor prohibit when oif specified 2026-10-19 3:24 ` [RFC net-next 1/2] " liamsmith827 @ 2026-10-25 17:13 ` Ido Schimmel 0 siblings, 0 replies; 4+ messages in thread From: Ido Schimmel @ 2026-10-25 17:13 UTC (permalink / raw) To: liamsmith827 Cc: netdev, dsahern, davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, liamsmith827 On Mon, Oct 19, 2026 at 03:24:05AM +0000, liamsmith827 wrote: > From: liamsmith827 <liamsmith827@proton.me> > > When flowi4_oif is set (SO_BINDTODEVICE, IP_PKTINFO, IP_UNICAST_IF, > etc), prohibit rules and routes cause fib_lookup() to return -EACCES, > but still allow the packet (and any necessary ARP) to be transmitted. > This is documented as the expected behavior in the tests, but is > confusing for route and rule maintainers. Specifically, Android uses > prohibit rules as the basis for its VPN "kill switch", with the > expectation that any packet encountering such a rule will not be allowed > out. Due to this interface binding exemption, apps can trivially bypass > the kill switch. This is most serious on cellular networks, where a > connection to an arbitrary IPv4 address can be established without > requiring ARP resolution. > > Preserve -EACCES from fib_lookup() instead of constructing an on-link > route when an output interface is specified. This honors prohibit rules > and routes while retaining the existing fallback for other lookup > errors. My understanding is that the original intent was to only ignore the "no route" error (-ENETUNREACH). The comment in the code says so: "we are allowed to send to iface even if it has NO routes". Ignoring unreachable, blackhole and prohibit routes / rules is merely a side-effect of not checking for a specific error code. I find it very hard to believe that anyone is relying on this behavior: 1. It's confusing. The ip-route man page says that unreachable, blackhole and prohibit routes all discard packets. 2. These rules and routes are honored in the VRF case. 3. IPv6 behaves differently. These rules are honored, but IPv6 will backtrack to a shorter prefix route if it hits an unreachable, blackhole or prohibit route with a strict oif. Will look into it, looks like a bug. Given the above, I think that the patch is too specific to your use case. It doesn't make sense to only honor prohibit rules / routes. How about checking for 'err == -ENETUNREACH' [1]? It doesn't allow us to distinguish between "no route" and "unreachable rule", but we can probably ignore it given that it has always been the case and nobody complained. It needs to be explained in the commit message. Note that multicast needs to be exempted because of VRF. The lookup fails on the unreachable default route in the VRF table (-EHOSTUNREACH) and we construct the route anyway. It is also consistent with IPv6 where these routes / rules do not affect multicast traffic since the per-interface ff00::/8 routes in the local table are matched first. This change will allow you to uncomment all the test cases in ipv4_ping_novrf(), so that it matches ipv6_ping_novrf(). > > Signed-off-by: liamsmith827 <liamsmith827@proton.me> > --- > net/ipv4/route.c | 11 ++++++----- > 1 file changed, 6 insertions(+), 5 deletions(-) > > diff --git a/net/ipv4/route.c b/net/ipv4/route.c > index 595a3b7f1783..616c20299334 100644 > --- a/net/ipv4/route.c > +++ b/net/ipv4/route.c > @@ -2853,7 +2853,7 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, > if (err) { > res->fi = NULL; > res->table = NULL; > - if (fl4->flowi4_oif && > + if (fl4->flowi4_oif && err != -EACCES && > (ipv4_is_multicast(fl4->daddr) || !fl4->flowi4_l3mdev)) { > /* Apparently, routing tables are wrong. Assume, > * that the destination is on link. > @@ -2864,13 +2864,14 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, > * addresses. When oif is specified, routing > * tables are looked up with only one purpose: > * to catch if destination is gatewayed, rather than > - * direct. Moreover, if MSG_DONTROUTE is set, > - * we send packet, ignoring both routing tables > - * and ifaddr state. --ANK > - * > + * direct. Why modify this hunk? Looks like an unrelated change. > * > * We could make it even if oif is unknown, > * likely IPv6, but we do not. > + * > + * Additionally, when oif is specified, routing lookup > + * allows us to check if destination is prohibited > + * (-EACCES). LS. Please remove the signature. It's unnecessary in the git era. > */ [1] diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 595a3b7f1783..b10ea8bec090 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2854,7 +2854,8 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, res->fi = NULL; res->table = NULL; if (fl4->flowi4_oif && - (ipv4_is_multicast(fl4->daddr) || !fl4->flowi4_l3mdev)) { + (ipv4_is_multicast(fl4->daddr) || + (!fl4->flowi4_l3mdev && err == -ENETUNREACH))) { /* Apparently, routing tables are wrong. Assume, * that the destination is on link. * ^ permalink raw reply [flat|nested] 4+ messages in thread
* [RFC net-next 2/2] selftests: net: Test prohibit when oif specified 2026-10-19 3:24 [RFC net-next 0/2] ipv4: Honor prohibit when oif specified liamsmith827 2026-10-19 3:24 ` [RFC net-next 1/2] " liamsmith827 @ 2026-10-19 3:24 ` liamsmith827 1 sibling, 0 replies; 4+ messages in thread From: liamsmith827 @ 2026-10-19 3:24 UTC (permalink / raw) To: netdev Cc: dsahern, idosch, davem, edumazet, kuba, pabeni, horms, shuah, linux-kselftest, linux-kernel, liamsmith827 From: liamsmith827 <liamsmith827@proton.me> Now that prohibit is honored when oif is specified, enable the existing test that expects an interface-bound ping to fail. Signed-off-by: liamsmith827 <liamsmith827@proton.me> --- tools/testing/selftests/net/fcnal-test.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index a50609535fed..7eb4dc0311a9 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -643,10 +643,9 @@ ipv4_ping_novrf() run_cmd ping -c1 -w1 ${a} log_test_addr ${a} $? 2 "ping out, blocked by rule" - # NOTE: ipv4 actually allows the lookup to fail and yet still create - # a viable rtable if the oif (e.g., bind to device) is set, so this - # case succeeds despite the rule - # run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a} + log_start + run_cmd ping -c1 -w1 -I ${NSA_DEV} ${a} + log_test_addr ${a} $? 2 "ping out, device bind, blocked by rule" a=${NSA_LO_IP} log_start -- 2.47.3 ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-25 17:14 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-19 3:24 [RFC net-next 0/2] ipv4: Honor prohibit when oif specified liamsmith827 2026-10-19 3:24 ` [RFC net-next 1/2] " liamsmith827 2026-10-25 17:13 ` Ido Schimmel 2026-10-19 3:24 ` [RFC net-next 2/2] selftests: net: Test " liamsmith827
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®