Add local rules for local networks

When a network is a local network, two new IP rules
should be installed :
• A rule that keeps the traffic to directly connected IPs
  working when the catch-all local rule is removed. This
  rule has PRIORITY_LOCAL_NETWORK and matches on the
  explicit bit being off, because the explicit rule will
  be installed separately by ConnectivityService in a
  followup patch at priority EXPLICIT_NETWORK.
20000: from all fwmark 0x0/0x10000 lookup xxxx
• A rule that matches on LOCAL_NET_ID explicitly pointing
  to the same table to keep traffic to netId 99 working.
  This is necessary for the continued working of dnsmasq,
  and might be necessary to keep traffic sent to 99 by
  other means on OEM devices.
16000: from all fwmark 0x10063/0x1ffff iif lo lookup xxxx

Test: new tests in this patch
Change-Id: If8729fc6f3716a580c936584b851bc38000b5de5
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 86b23b6..97ca84e 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -595,6 +595,34 @@
                         mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd);
 }
 
+// A rule to route traffic based on an local network.
+//
+// Supports apps that send traffic to local IPs without binding to a particular network.
+//
+[[nodiscard]] static int modifyLocalNetworkRule(uint32_t table, bool add) {
+    Fwmark fwmark;
+    Fwmark mask;
+
+    fwmark.explicitlySelected = false;
+    mask.explicitlySelected = true;
+
+    if (const int ret = modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_LOCAL_NETWORK,
+                                     table, fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE,
+                                     INVALID_UID, INVALID_UID)) {
+        return ret;
+    }
+
+    fwmark.explicitlySelected = true;
+    mask.explicitlySelected = true;
+
+    fwmark.netId = INetd::LOCAL_NET_ID;
+    mask.netId = FWMARK_NET_ID_MASK;
+
+    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_EXPLICIT_NETWORK, table,
+                        fwmark.intValue, mask.intValue, IIF_LOOPBACK, OIF_NONE, INVALID_UID,
+                        INVALID_UID);
+}
+
 // A rule to route traffic based on a chosen outgoing interface.
 //
 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already knows
@@ -858,7 +886,7 @@
 /* static */
 int RouteController::modifyPhysicalNetwork(unsigned netId, const char* interface,
                                            const UidRangeMap& uidRangeMap, Permission permission,
-                                           bool add, bool modifyNonUidBasedRules) {
+                                           bool add, bool modifyNonUidBasedRules, bool local) {
     uint32_t table = getRouteTableForInterface(interface, false /* local */);
     if (table == RT_TABLE_UNSPEC) {
         return -ESRCH;
@@ -904,6 +932,11 @@
                                             UidRanges::SUB_PRIORITY_HIGHEST, add)) {
         return ret;
     }
+    if (local) {
+        if (const int ret = modifyLocalNetworkRule(table, add)) {
+            return ret;
+        }
+    }
     if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID,
                                              UidRanges::SUB_PRIORITY_HIGHEST, add)) {
         return ret;
@@ -1302,9 +1335,9 @@
 
 int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
                                                    Permission permission,
-                                                   const UidRangeMap& uidRangeMap) {
+                                                   const UidRangeMap& uidRangeMap, bool local) {
     if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_ADD,
-                                        MODIFY_NON_UID_BASED_RULES)) {
+                                        MODIFY_NON_UID_BASED_RULES, local)) {
         return ret;
     }
 
@@ -1320,9 +1353,10 @@
 
 int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
                                                         Permission permission,
-                                                        const UidRangeMap& uidRangeMap) {
+                                                        const UidRangeMap& uidRangeMap,
+                                                        bool local) {
     if (int ret = modifyPhysicalNetwork(netId, interface, uidRangeMap, permission, ACTION_DEL,
-                                        MODIFY_NON_UID_BASED_RULES)) {
+                                        MODIFY_NON_UID_BASED_RULES, local)) {
         return ret;
     }
 
@@ -1366,17 +1400,17 @@
 
 int RouteController::modifyPhysicalNetworkPermission(unsigned netId, const char* interface,
                                                      Permission oldPermission,
-                                                     Permission newPermission) {
+                                                     Permission newPermission, bool local) {
     // Physical network rules either use permission bits or UIDs, but not both.
     // So permission changes don't affect any UID-based rules.
     UidRangeMap emptyUidRangeMap;
     // Add the new rules before deleting the old ones, to avoid race conditions.
     if (int ret = modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, newPermission,
-                                        ACTION_ADD, MODIFY_NON_UID_BASED_RULES)) {
+                                        ACTION_ADD, MODIFY_NON_UID_BASED_RULES, local)) {
         return ret;
     }
     return modifyPhysicalNetwork(netId, interface, emptyUidRangeMap, oldPermission, ACTION_DEL,
-                                 MODIFY_NON_UID_BASED_RULES);
+                                 MODIFY_NON_UID_BASED_RULES, local);
 }
 
 int RouteController::addUsersToRejectNonSecureNetworkRule(const UidRanges& uidRanges) {
@@ -1500,15 +1534,15 @@
 }
 
 int RouteController::addUsersToPhysicalNetwork(unsigned netId, const char* interface,
-                                               const UidRangeMap& uidRangeMap) {
+                                               const UidRangeMap& uidRangeMap, bool local) {
     return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_ADD,
-                                 !MODIFY_NON_UID_BASED_RULES);
+                                 !MODIFY_NON_UID_BASED_RULES, local);
 }
 
 int RouteController::removeUsersFromPhysicalNetwork(unsigned netId, const char* interface,
-                                                    const UidRangeMap& uidRangeMap) {
+                                                    const UidRangeMap& uidRangeMap, bool local) {
     return modifyPhysicalNetwork(netId, interface, uidRangeMap, PERMISSION_NONE, ACTION_DEL,
-                                 !MODIFY_NON_UID_BASED_RULES);
+                                 !MODIFY_NON_UID_BASED_RULES, local);
 }
 
 int RouteController::addUsersToUnreachableNetwork(unsigned netId, const UidRangeMap& uidRangeMap) {