Add IPv4 link-local multicast range to local routing tables

This commit allows local multicast traffic to be sent locally
instead of being sent through VPN when using a VPN automatic
bypass for local traffic.

Currently, the local network that is considered in VPN local
exclusion mode is the same subnet of the network assigned
address. If apps try to make some traffic to multicast range,
it may be routed to VPN and block the traffic. E.g. If app
connect a UDP socket to multicast range(224.0.0.x) and sends
from the socket, or app send to 224.0.0.x from an unconnected
socket. The traffic will send from VPN network. This traffic
may not be well-routed in VPN network. So the case should be
also considered to make the function work in the VPN bypass
mode because it usually won't be the network assigned subnet
range. Add the multicast range as a fixed range in the local
exclusion table.

The multicast range is 224.0.0.0/4 but only limit it to
224.0.0.0/24 since the IPv4 definitions are not as precise as
for IPv6, it is the only range that the standards (RFC 2365
and RFC 5771) specify is link-local and must not be forwarded.

Bug: 243200566
Test: cd system/netd ; atest
Test: connect to Wifi or cellular network and check the routing
Test: manually test with chromecast with local routes exclusion
      enabled
Change-Id: I79fe499fb02a88ec687fadf3fad461c204fe3e47
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index dca6bd9..86b23b6 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -667,6 +667,19 @@
                         INVALID_UID);
 }
 
+int RouteController::addFixedLocalRoutes(const char* interface) {
+    for (size_t i = 0; i < ARRAY_SIZE(V4_FIXED_LOCAL_PREFIXES); ++i) {
+        if (int ret = modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface,
+                                  V4_FIXED_LOCAL_PREFIXES[i], nullptr /* nexthop */,
+                                  RouteController::INTERFACE, 0 /* mtu */, 0 /* priority */,
+                                  true /* isLocal */)) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
 // A rule to enable split tunnel VPNs.
 //
 // If a packet with a VPN's netId doesn't find a route in the VPN's routing table, it's allowed to
@@ -1297,6 +1310,11 @@
 
     maybeModifyQdiscClsact(interface, ACTION_ADD);
     updateTableNamesFile();
+
+    if (int ret = addFixedLocalRoutes(interface)) {
+        return ret;
+    }
+
     return 0;
 }
 
diff --git a/server/RouteController.h b/server/RouteController.h
index f57bc24..1b3a093 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -85,6 +85,13 @@
 constexpr int32_t RULE_PRIORITY_UNREACHABLE                       = 32000;
 // clang-format on
 
+static const char* V4_FIXED_LOCAL_PREFIXES[] = {
+        // The multicast range is 224.0.0.0/4 but only limit it to 224.0.0.0/24 since the IPv4
+        // definitions are not as precise as for IPv6, it is the only range that the standards
+        // (RFC 2365 and RFC 5771) specify is link-local and must not be forwarded.
+        "224.0.0.0/24"  // Link-local multicast; non-internet routable
+};
+
 class UidRanges;
 
 class RouteController {
@@ -232,6 +239,7 @@
                                          bool add);
     static bool isLocalRoute(TableType tableType, const char* destination, const char* nexthop);
     static bool isWithinIpv4LocalPrefix(const char* addrstr);
+    static int addFixedLocalRoutes(const char* interface);
 };
 
 // Public because they are called by by RouteControllerTest.cpp.
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 52608f9..a63788d 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -137,6 +137,7 @@
 using android::net::TunInterface;
 using android::net::UidRangeParcel;
 using android::net::UidRanges;
+using android::net::V4_FIXED_LOCAL_PREFIXES;
 using android::net::mdns::aidl::DiscoveryInfo;
 using android::net::mdns::aidl::GetAddressInfo;
 using android::net::mdns::aidl::IMDns;
@@ -1701,6 +1702,13 @@
     EXPECT_TRUE(mNetd->networkSetDefault(TEST_NETID1).isOk());
 
     std::string localTableName = std::string(sTun.name() + "_local");
+
+    // Verify the fixed routes exist in the local table.
+    for (size_t i = 0; i < std::size(V4_FIXED_LOCAL_PREFIXES); i++) {
+        expectNetworkRouteExists(IP_RULE_V4, sTun.name(), V4_FIXED_LOCAL_PREFIXES[i], "",
+                                 localTableName.c_str());
+    }
+
     // Set up link-local routes for connectivity to the "gateway"
     for (size_t i = 0; i < std::size(kDirectlyConnectedRoutes); i++) {
         const auto& td = kDirectlyConnectedRoutes[i];