netd: Route MTU

- Route may include optional MTU parameter
- Change route is added so routes don't need to be deleted then re-added
- Add/Del/Change functions to pass route info as parcel

Bug: 142892223
Test: new unit tests
Change-Id: Idc32ecb0520b1f4136b3fe0e3f7b6800fb3005a6
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 90fd602..66baace 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -1635,26 +1635,52 @@
 namespace {
 
 std::string ipRouteString(const std::string& ifName, const std::string& dst,
-                          const std::string& nextHop) {
+                          const std::string& nextHop, const std::string& mtu) {
     std::string dstString = (dst == "0.0.0.0/0" || dst == "::/0") ? "default" : dst;
 
     if (!nextHop.empty()) {
         dstString += " via " + nextHop;
     }
 
-    return dstString + " dev " + ifName;
+    dstString += " dev " + ifName;
+
+    if (!mtu.empty()) {
+        dstString += " proto static";
+        // IPv6 routes report the metric, IPv4 routes report the scope.
+        // TODO: move away from specifying the entire string and use a regexp instead.
+        if (dst.find(':') != std::string::npos) {
+            dstString += " metric 1024";
+        } else {
+            if (nextHop.empty()) {
+                dstString += " scope link";
+            }
+        }
+        dstString += " mtu " + mtu;
+    }
+
+    return dstString;
+}
+
+void expectNetworkRouteExistsWithMtu(const char* ipVersion, const std::string& ifName,
+                                     const std::string& dst, const std::string& nextHop,
+                                     const std::string& mtu, const char* table) {
+    std::string routeString = ipRouteString(ifName, dst, nextHop, mtu);
+    EXPECT_TRUE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop, mtu)))
+            << "Couldn't find route to " << dst << ": '" << routeString << "' in table " << table;
 }
 
 void expectNetworkRouteExists(const char* ipVersion, const std::string& ifName,
                               const std::string& dst, const std::string& nextHop,
                               const char* table) {
-    EXPECT_TRUE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop)));
+    expectNetworkRouteExistsWithMtu(ipVersion, ifName, dst, nextHop, "", table);
 }
 
 void expectNetworkRouteDoesNotExist(const char* ipVersion, const std::string& ifName,
                                     const std::string& dst, const std::string& nextHop,
                                     const char* table) {
-    EXPECT_FALSE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop)));
+    std::string routeString = ipRouteString(ifName, dst, nextHop, "");
+    EXPECT_FALSE(ipRouteExists(ipVersion, table, ipRouteString(ifName, dst, nextHop, "")))
+            << "Found unexpected route " << routeString << " in table " << table;
 }
 
 bool ipRuleExists(const char* ipVersion, const std::string& ipRule) {
@@ -1886,6 +1912,46 @@
         }
     }
 
+    for (size_t i = 0; i < std::size(kTestData); i++) {
+        const auto& td = kTestData[i];
+        int mtu = (i % 2) ? 1480 : 1280;
+
+        android::net::RouteInfoParcel parcel;
+        parcel.ifName = sTun.name();
+        parcel.destination = td.testDest;
+        parcel.nextHop = td.testNextHop;
+        parcel.mtu = mtu;
+        binder::Status status = mNetd->networkAddRouteParcel(TEST_NETID1, parcel);
+        if (td.expectSuccess) {
+            EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+            expectNetworkRouteExistsWithMtu(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                            std::to_string(parcel.mtu), sTun.name().c_str());
+        } else {
+            EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+            EXPECT_NE(0, status.serviceSpecificErrorCode());
+        }
+
+        parcel.mtu = 1337;
+        status = mNetd->networkUpdateRouteParcel(TEST_NETID1, parcel);
+        if (td.expectSuccess) {
+            EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+            expectNetworkRouteExistsWithMtu(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                            std::to_string(parcel.mtu), sTun.name().c_str());
+        } else {
+            EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+            EXPECT_NE(0, status.serviceSpecificErrorCode());
+        }
+
+        status = mNetd->networkRemoveRouteParcel(TEST_NETID1, parcel);
+        if (td.expectSuccess) {
+            EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+            expectNetworkRouteDoesNotExist(td.ipVersion, sTun.name(), td.testDest, td.testNextHop,
+                                           sTun.name().c_str());
+        } else {
+            EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+            EXPECT_NE(0, status.serviceSpecificErrorCode());
+        }
+    }
     // Remove test physical network
     EXPECT_TRUE(mNetd->networkDestroy(TEST_NETID1).isOk());
 }