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/server/Android.bp b/server/Android.bp
index ad4e4ff..479dea7 100644
--- a/server/Android.bp
+++ b/server/Android.bp
@@ -31,6 +31,7 @@
"binder/android/net/INetdUnsolicitedEventListener.aidl",
"binder/android/net/InterfaceConfigurationParcel.aidl",
"binder/android/net/MarkMaskParcel.aidl",
+ "binder/android/net/RouteInfoParcel.aidl",
"binder/android/net/TetherConfigParcel.aidl",
"binder/android/net/TetherStatsParcel.aidl",
"binder/android/net/UidRangeParcel.aidl",
diff --git a/server/NetdHwService.cpp b/server/NetdHwService.cpp
index b209aca..15855da 100644
--- a/server/NetdHwService.cpp
+++ b/server/NetdHwService.cpp
@@ -108,7 +108,8 @@
RETURN_IF_NOT_OEM_NETWORK(netId);
return toHalStatus(gCtls->netCtrl.addRoute(netId, ifname.c_str(), destination.c_str(),
- maybeNullString(nexthop), false, INVALID_UID));
+ maybeNullString(nexthop), false, INVALID_UID,
+ 0 /* mtu */));
}
Return <StatusCode> NetdHwService::removeRouteFromOemNetwork(
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 322604a..c951c24 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -969,6 +969,39 @@
return binder::Status::ok();
}
+binder::Status NetdNativeService::networkAddRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ // Public methods of NetworkController are thread-safe.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+ bool legacy = false;
+ uid_t uid = 0; // UID is only meaningful for legacy routes.
+
+ // convert Parcel to parameters
+ int res = gCtls->netCtrl.addRoute(netId, route.ifName.c_str(), route.destination.c_str(),
+ route.nextHop.empty() ? nullptr : route.nextHop.c_str(),
+ legacy, uid, route.mtu);
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::networkUpdateRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ // Public methods of NetworkController are thread-safe.
+ ENFORCE_NETWORK_STACK_PERMISSIONS();
+ bool legacy = false;
+ uid_t uid = 0; // UID is only meaningful for legacy routes.
+
+ // convert Parcel to parameters
+ int res = gCtls->netCtrl.updateRoute(netId, route.ifName.c_str(), route.destination.c_str(),
+ route.nextHop.empty() ? nullptr : route.nextHop.c_str(),
+ legacy, uid, route.mtu);
+ return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::networkRemoveRouteParcel(int32_t netId,
+ const RouteInfoParcel& route) {
+ return networkRemoveRoute(netId, route.ifName, route.destination, route.nextHop);
+}
+
binder::Status NetdNativeService::networkAddRoute(int32_t netId, const std::string& ifName,
const std::string& destination,
const std::string& nextHop) {
@@ -977,7 +1010,7 @@
bool legacy = false;
uid_t uid = 0; // UID is only meaningful for legacy routes.
int res = gCtls->netCtrl.addRoute(netId, ifName.c_str(), destination.c_str(),
- nextHop.empty() ? nullptr : nextHop.c_str(), legacy, uid);
+ nextHop.empty() ? nullptr : nextHop.c_str(), legacy, uid, 0);
return statusFromErrcode(res);
}
@@ -999,7 +1032,7 @@
bool legacy = true;
int res = gCtls->netCtrl.addRoute(netId, ifName.c_str(), destination.c_str(),
nextHop.empty() ? nullptr : nextHop.c_str(), legacy,
- (uid_t) uid);
+ (uid_t)uid, 0);
return statusFromErrcode(res);
}
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 12dbd37..34d6215 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -76,6 +76,9 @@
const std::vector<UidRangeParcel>& uids) override;
binder::Status networkRejectNonSecureVpn(bool enable,
const std::vector<UidRangeParcel>& uids) override;
+ binder::Status networkAddRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
+ binder::Status networkUpdateRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
+ binder::Status networkRemoveRouteParcel(int32_t netId, const RouteInfoParcel& route) override;
binder::Status networkAddRoute(int32_t netId, const std::string& ifName,
const std::string& destination,
const std::string& nextHop) override;
diff --git a/server/NetlinkCommands.h b/server/NetlinkCommands.h
index c07194d..11fb8e0 100644
--- a/server/NetlinkCommands.h
+++ b/server/NetlinkCommands.h
@@ -28,6 +28,7 @@
const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
const uint16_t NETLINK_ROUTE_CREATE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
+const uint16_t NETLINK_ROUTE_REPLACE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_REPLACE;
// Don't create rules with NLM_F_EXCL, because operations such as changing network permissions rely
// on make-before-break. The kernel did not complain about duplicate rules until ~4.9, at which
// point it started returning EEXIST. See for example b/69607866 . We can't just ignore the EEXIST
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 3e428ff..6a11f92 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -585,13 +585,18 @@
}
int NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool legacy, uid_t uid) {
- return modifyRoute(netId, interface, destination, nexthop, true, legacy, uid);
+ const char* nexthop, bool legacy, uid_t uid, int mtu) {
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_ADD, legacy, uid, mtu);
+}
+
+int NetworkController::updateRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid, int mtu) {
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_UPDATE, legacy, uid, mtu);
}
int NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
const char* nexthop, bool legacy, uid_t uid) {
- return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
+ return modifyRoute(netId, interface, destination, nexthop, ROUTE_REMOVE, legacy, uid, 0);
}
void NetworkController::addInterfaceAddress(unsigned ifIndex, const char* address) {
@@ -768,7 +773,8 @@
}
int NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add, bool legacy, uid_t uid) {
+ const char* nexthop, enum RouteOperation op, bool legacy,
+ uid_t uid, int mtu) {
ScopedRLock lock(mRWLock);
if (!isValidNetworkLocked(netId)) {
@@ -798,8 +804,15 @@
tableType = RouteController::INTERFACE;
}
- return add ? RouteController::addRoute(interface, destination, nexthop, tableType) :
- RouteController::removeRoute(interface, destination, nexthop, tableType);
+ switch (op) {
+ case ROUTE_ADD:
+ return RouteController::addRoute(interface, destination, nexthop, tableType, mtu);
+ case ROUTE_UPDATE:
+ return RouteController::updateRoute(interface, destination, nexthop, tableType, mtu);
+ case ROUTE_REMOVE:
+ return RouteController::removeRoute(interface, destination, nexthop, tableType);
+ }
+ return -EINVAL;
}
int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) {
diff --git a/server/NetworkController.h b/server/NetworkController.h
index e0abde0..ff49c02 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -88,6 +88,9 @@
static constexpr int LOCAL_NET_ID = INetd::LOCAL_NET_ID;
static constexpr int DUMMY_NET_ID = 51;
+ // Route mode for modify route
+ enum RouteOperation { ROUTE_ADD, ROUTE_UPDATE, ROUTE_REMOVE };
+
NetworkController();
unsigned getDefaultNetwork() const;
@@ -122,7 +125,9 @@
// Routes are added to tables determined by the interface, so only |interface| is actually used.
// |netId| is given only to sanity check that the interface has the correct netId.
[[nodiscard]] int addRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool legacy, uid_t uid);
+ const char* nexthop, bool legacy, uid_t uid, int mtu);
+ [[nodiscard]] int updateRoute(unsigned netId, const char* interface, const char* destination,
+ const char* nexthop, bool legacy, uid_t uid, int mtu);
[[nodiscard]] int removeRoute(unsigned netId, const char* interface, const char* destination,
const char* nexthop, bool legacy, uid_t uid);
@@ -158,7 +163,8 @@
[[nodiscard]] int createPhysicalNetworkLocked(unsigned netId, Permission permission);
[[nodiscard]] int modifyRoute(unsigned netId, const char* interface, const char* destination,
- const char* nexthop, bool add, bool legacy, uid_t uid);
+ const char* nexthop, RouteOperation op, bool legacy, uid_t uid,
+ int mtu);
[[nodiscard]] int modifyFallthroughLocked(unsigned vpnNetId, bool add);
void updateTcpSocketMonitorPolling();
diff --git a/server/PhysicalNetwork.cpp b/server/PhysicalNetwork.cpp
index 56f4ac3..2808fbe 100644
--- a/server/PhysicalNetwork.cpp
+++ b/server/PhysicalNetwork.cpp
@@ -88,8 +88,8 @@
// If any of these operations fail, there's no point in logging because RouteController will
// have already logged a message. There's also no point returning an error since there's
// nothing we can do.
- (void) RouteController::addRoute(interface.c_str(), dst, "throw",
- RouteController::INTERFACE);
+ (void)RouteController::addRoute(interface.c_str(), dst, "throw", RouteController::INTERFACE,
+ 0 /* mtu */);
(void) RouteController::removeRoute(interface.c_str(), dst, "throw",
RouteController::INTERFACE);
}
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index a62ed6b..c4c3cfb 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -118,6 +118,13 @@
rtattr RTATTR_OIF = { U16_RTA_LENGTH(sizeof(uint32_t)), RTA_OIF };
rtattr RTATTR_PRIO = { U16_RTA_LENGTH(sizeof(uint32_t)), RTA_PRIORITY };
+// One or more nested attributes in the RTA_METRICS attribute.
+rtattr RTATTRX_MTU = { U16_RTA_LENGTH(sizeof(uint32_t)), RTAX_MTU};
+
+// The RTA_METRICS attribute itself.
+constexpr int RTATTR_METRICS_SIZE = sizeof(RTATTRX_MTU) + sizeof(uint32_t);
+rtattr RTATTR_METRICS = { U16_RTA_LENGTH(RTATTR_METRICS_SIZE), RTA_METRICS };
+
uint8_t PADDING_BUFFER[RTA_ALIGNTO] = {0, 0, 0, 0};
// END CONSTANTS ----------------------------------------------------------------------------------
@@ -339,8 +346,8 @@
// Adds or deletes an IPv4 or IPv6 route.
// Returns 0 on success or negative errno on failure.
-int modifyIpRoute(uint16_t action, uint32_t table, const char* interface, const char* destination,
- const char* nexthop) {
+int modifyIpRoute(uint16_t action, uint16_t flags, uint32_t table, const char* interface,
+ const char* destination, const char* nexthop, uint32_t mtu) {
// At least the destination must be non-null.
if (!destination) {
ALOGE("null destination");
@@ -410,22 +417,23 @@
rtattr rtaGateway = { U16_RTA_LENGTH(rawLength), RTA_GATEWAY };
iovec iov[] = {
- { nullptr, 0 },
- { &route, sizeof(route) },
- { &RTATTR_TABLE, sizeof(RTATTR_TABLE) },
- { &table, sizeof(table) },
- { &rtaDst, sizeof(rtaDst) },
- { rawAddress, static_cast<size_t>(rawLength) },
- { &RTATTR_OIF, interface != OIF_NONE ? sizeof(RTATTR_OIF) : 0 },
- { &ifindex, interface != OIF_NONE ? sizeof(ifindex) : 0 },
- { &rtaGateway, nexthop ? sizeof(rtaGateway) : 0 },
- { rawNexthop, nexthop ? static_cast<size_t>(rawLength) : 0 },
- { &RTATTR_PRIO, isDefaultThrowRoute ? sizeof(RTATTR_PRIO) : 0 },
- { &PRIO_THROW, isDefaultThrowRoute ? sizeof(PRIO_THROW) : 0 },
+ { nullptr, 0 },
+ { &route, sizeof(route) },
+ { &RTATTR_TABLE, sizeof(RTATTR_TABLE) },
+ { &table, sizeof(table) },
+ { &rtaDst, sizeof(rtaDst) },
+ { rawAddress, static_cast<size_t>(rawLength) },
+ { &RTATTR_OIF, interface != OIF_NONE ? sizeof(RTATTR_OIF) : 0 },
+ { &ifindex, interface != OIF_NONE ? sizeof(ifindex) : 0 },
+ { &rtaGateway, nexthop ? sizeof(rtaGateway) : 0 },
+ { rawNexthop, nexthop ? static_cast<size_t>(rawLength) : 0 },
+ { &RTATTR_METRICS, mtu != 0 ? sizeof(RTATTR_METRICS) : 0 },
+ { &RTATTRX_MTU, mtu != 0 ? sizeof(RTATTRX_MTU) : 0 },
+ { &mtu, mtu != 0 ? sizeof(mtu) : 0 },
+ { &RTATTR_PRIO, isDefaultThrowRoute ? sizeof(RTATTR_PRIO) : 0 },
+ { &PRIO_THROW, isDefaultThrowRoute ? sizeof(PRIO_THROW) : 0 },
};
- uint16_t flags = (action == RTM_NEWROUTE) ? NETLINK_ROUTE_CREATE_FLAGS : NETLINK_REQUEST_FLAGS;
-
// Allow creating multiple link-local routes in the same table, so we can make IPv6
// work on all interfaces in the local_network table.
if (family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(reinterpret_cast<in6_addr*>(rawAddress))) {
@@ -697,11 +705,13 @@
return ret;
}
- if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "0.0.0.0/0", nullptr))) {
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table, interface,
+ "0.0.0.0/0", nullptr, 0 /* mtu */))) {
return ret;
}
- if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "::/0", nullptr))) {
+ if ((ret = modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table, interface, "::/0",
+ nullptr, 0 /* mtu */))) {
return ret;
}
@@ -859,8 +869,9 @@
// Adds or removes an IPv4 or IPv6 route to the specified table.
// Returns 0 on success or negative errno on failure.
-int RouteController::modifyRoute(uint16_t action, const char* interface, const char* destination,
- const char* nexthop, TableType tableType) {
+int RouteController::modifyRoute(uint16_t action, uint16_t flags, const char* interface,
+ const char* destination, const char* nexthop, TableType tableType,
+ int mtu) {
uint32_t table;
switch (tableType) {
case RouteController::INTERFACE: {
@@ -884,7 +895,7 @@
}
}
- int ret = modifyIpRoute(action, table, interface, destination, nexthop);
+ int ret = modifyIpRoute(action, flags, table, interface, destination, nexthop, mtu);
// Trying to add a route that already exists shouldn't cause an error.
if (ret && !(action == RTM_NEWROUTE && ret == -EEXIST)) {
return ret;
@@ -1066,13 +1077,21 @@
}
int RouteController::addRoute(const char* interface, const char* destination, const char* nexthop,
- TableType tableType) {
- return modifyRoute(RTM_NEWROUTE, interface, destination, nexthop, tableType);
+ TableType tableType, int mtu) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, interface, destination, nexthop,
+ tableType, mtu);
}
int RouteController::removeRoute(const char* interface, const char* destination,
const char* nexthop, TableType tableType) {
- return modifyRoute(RTM_DELROUTE, interface, destination, nexthop, tableType);
+ return modifyRoute(RTM_DELROUTE, NETLINK_REQUEST_FLAGS, interface, destination, nexthop,
+ tableType, 0);
+}
+
+int RouteController::updateRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType, int mtu) {
+ return modifyRoute(RTM_NEWROUTE, NETLINK_ROUTE_REPLACE_FLAGS, interface, destination, nexthop,
+ tableType, mtu);
}
int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
diff --git a/server/RouteController.h b/server/RouteController.h
index e36320f..656fc21 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -90,9 +90,11 @@
// |nexthop| can be NULL (to indicate a directly-connected route), "unreachable" (to indicate a
// route that's blocked), "throw" (to indicate the lack of a match), or a regular IP address.
[[nodiscard]] static int addRoute(const char* interface, const char* destination,
- const char* nexthop, TableType tableType);
+ const char* nexthop, TableType tableType, int mtu);
[[nodiscard]] static int removeRoute(const char* interface, const char* destination,
const char* nexthop, TableType tableType);
+ [[nodiscard]] static int updateRoute(const char* interface, const char* destination,
+ const char* nexthop, TableType tableType, int mtu);
[[nodiscard]] static int enableTethering(const char* inputInterface,
const char* outputInterface);
@@ -125,8 +127,9 @@
static int modifyDefaultNetwork(uint16_t action, const char* interface, Permission permission);
static int modifyPhysicalNetwork(unsigned netId, const char* interface, Permission permission,
bool add);
- static int modifyRoute(uint16_t action, const char* interface, const char* destination,
- const char* nexthop, TableType tableType);
+ static int modifyRoute(uint16_t action, uint16_t flags, const char* interface,
+ const char* destination, const char* nexthop, TableType tableType,
+ int mtu);
static int modifyTetheredNetwork(uint16_t action, const char* inputInterface,
const char* outputInterface);
static int modifyVpnFallthroughRule(uint16_t action, unsigned vpnNetId,
@@ -140,8 +143,9 @@
// Public because they are called by by RouteControllerTest.cpp.
// TODO: come up with a scheme of unit testing this code that does not rely on making all its
// functions public.
-[[nodiscard]] int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
- const char* destination, const char* nexthop);
+[[nodiscard]] int modifyIpRoute(uint16_t action, uint16_t flags, uint32_t table,
+ const char* interface, const char* destination, const char* nexthop,
+ uint32_t mtu);
uint32_t getRulePriority(const nlmsghdr *nlh);
[[nodiscard]] int modifyIncomingPacketMark(unsigned netId, const char* interface,
Permission permission, bool add);
diff --git a/server/RouteControllerTest.cpp b/server/RouteControllerTest.cpp
index 20b3618..fed15a3 100644
--- a/server/RouteControllerTest.cpp
+++ b/server/RouteControllerTest.cpp
@@ -79,18 +79,24 @@
static_assert(table2 < RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX,
"Test table2 number too large");
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.2/32", nullptr));
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.3/32", nullptr));
- EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table2, "lo", "192.0.2.4/32", nullptr));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo",
+ "192.0.2.2/32", nullptr, 0 /* mtu */));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo",
+ "192.0.2.3/32", nullptr, 0 /* mtu */));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, NETLINK_ROUTE_CREATE_FLAGS, table2, "lo",
+ "192.0.2.4/32", nullptr, 0 /* mtu */));
EXPECT_EQ(0, flushRoutes(table1));
EXPECT_EQ(-ESRCH,
- modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.2/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo", "192.0.2.2/32",
+ nullptr, 0 /* mtu */));
EXPECT_EQ(-ESRCH,
- modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.3/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table1, "lo", "192.0.2.3/32",
+ nullptr, 0 /* mtu */));
EXPECT_EQ(0,
- modifyIpRoute(RTM_DELROUTE, table2, "lo", "192.0.2.4/32", nullptr));
+ modifyIpRoute(RTM_DELROUTE, NETLINK_ROUTE_CREATE_FLAGS, table2, "lo", "192.0.2.4/32",
+ nullptr, 0 /* mtu */));
}
TEST_F(RouteControllerTest, TestModifyIncomingPacketMark) {
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index 8192bb9..cecba46 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -19,6 +19,7 @@
import android.net.INetdUnsolicitedEventListener;
import android.net.InterfaceConfigurationParcel;
import android.net.MarkMaskParcel;
+import android.net.RouteInfoParcel;
import android.net.TetherConfigParcel;
import android.net.TetherStatsParcel;
import android.net.UidRangeParcel;
@@ -1214,4 +1215,32 @@
* @return A MarkMaskParcel of the given network id.
*/
MarkMaskParcel getFwmarkForNetwork(int netId);
+
+ /**
+ * Add a route for specific network
+ *
+ * @param netId the network to add the route to
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkAddRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+
+ /**
+ * Update a route for specific network
+ *
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkUpdateRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
+
+ /**
+ * Remove a route for specific network
+ *
+ * @param routeInfo parcelable with route information
+ * @throws ServiceSpecificException in case of failure, with an error code indicating the
+ * cause of the failure.
+ */
+ void networkRemoveRouteParcel(int netId, in android.net.RouteInfoParcel routeInfo);
}
diff --git a/server/binder/android/net/RouteInfoParcel.aidl b/server/binder/android/net/RouteInfoParcel.aidl
new file mode 100644
index 0000000..fcc86e3
--- /dev/null
+++ b/server/binder/android/net/RouteInfoParcel.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable RouteInfoParcel {
+ // The destination of the route.
+ @utf8InCpp String destination;
+ // The name of interface of the route. This interface should be assigned to the netID.
+ @utf8InCpp String ifName;
+ // The route's next hop address, or one of the NEXTHOP_* constants defined in INetd.aidl.
+ @utf8InCpp String nextHop;
+ // The MTU of the route.
+ int mtu;
+}