diff options
Diffstat (limited to 'common/device/com/android/net/module/util')
4 files changed, 305 insertions, 0 deletions
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkConstants.java b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java index 07b52d8c..83a82b74 100644 --- a/common/device/com/android/net/module/util/netlink/NetlinkConstants.java +++ b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java @@ -146,12 +146,26 @@ public class NetlinkConstants { public static final int RTMGRP_LINK = 1; public static final int RTMGRP_IPV4_IFADDR = 0x10; public static final int RTMGRP_IPV6_IFADDR = 0x100; + public static final int RTMGRP_IPV6_ROUTE = 0x400; public static final int RTNLGRP_ND_USEROPT = 20; public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1); // Device flags. public static final int IFF_LOWER_UP = 1 << 16; + // Known values for struct rtmsg rtm_protocol. + public static final short RTPROT_KERNEL = 2; + public static final short RTPROT_RA = 9; + + // Known values for struct rtmsg rtm_scope. + public static final short RT_SCOPE_UNIVERSE = 0; + + // Known values for struct rtmsg rtm_type. + public static final short RTN_UNICAST = 1; + + // Known values for struct rtmsg rtm_flags. + public static final int RTM_F_CLONED = 0x200; + /** * Convert a netlink message type to a string for control message. */ diff --git a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java index 708736e5..a216752b 100644 --- a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java +++ b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java @@ -126,6 +126,9 @@ public class NetlinkMessage { case NetlinkConstants.RTM_NEWADDR: case NetlinkConstants.RTM_DELADDR: return (NetlinkMessage) RtNetlinkAddressMessage.parse(nlmsghdr, byteBuffer); + case NetlinkConstants.RTM_NEWROUTE: + case NetlinkConstants.RTM_DELROUTE: + return (NetlinkMessage) RtNetlinkRouteMessage.parse(nlmsghdr, byteBuffer); case NetlinkConstants.RTM_NEWNEIGH: case NetlinkConstants.RTM_DELNEIGH: case NetlinkConstants.RTM_GETNEIGH: diff --git a/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java b/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java new file mode 100644 index 00000000..c5efcb26 --- /dev/null +++ b/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2021 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 com.android.net.module.util.netlink; + +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; + +import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY; + +import android.net.IpPrefix; +import android.system.OsConstants; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.nio.ByteBuffer; + +/** + * A NetlinkMessage subclass for rtnetlink route messages. + * + * RtNetlinkRouteMessage.parse() must be called with a ByteBuffer that contains exactly one + * netlink message. + * + * see also: + * + * include/uapi/linux/rtnetlink.h + * + * @hide + */ +public class RtNetlinkRouteMessage extends NetlinkMessage { + public static final short RTA_DST = 1; + public static final short RTA_OIF = 4; + public static final short RTA_GATEWAY = 5; + + private int mIfindex; + @NonNull + private StructRtMsg mRtmsg; + @NonNull + private IpPrefix mDestination; + @Nullable + private InetAddress mGateway; + + private RtNetlinkRouteMessage(StructNlMsgHdr header) { + super(header); + mRtmsg = null; + mDestination = null; + mGateway = null; + mIfindex = 0; + } + + public int getInterfaceIndex() { + return mIfindex; + } + + @NonNull + public StructRtMsg getRtMsgHeader() { + return mRtmsg; + } + + @NonNull + public IpPrefix getDestination() { + return mDestination; + } + + @Nullable + public InetAddress getGateway() { + return mGateway; + } + + /** + * Check whether the address families of destination and gateway match rtm_family in + * StructRtmsg. + * + * For example, IPv4-mapped IPv6 addresses as an IPv6 address will be always converted to IPv4 + * address, that's incorrect when upper layer creates a new {@link RouteInfo} class instance + * for IPv6 route with the converted IPv4 gateway. + */ + private static boolean matchRouteAddressFamily(@NonNull final InetAddress address, + int family) { + return ((address instanceof Inet4Address) && (family == AF_INET)) + || ((address instanceof Inet6Address) && (family == AF_INET6)); + } + + /** + * Parse rtnetlink route message from {@link ByteBuffer}. This method must be called with a + * ByteBuffer that contains exactly one netlink message. + * + * @param header netlink message header. + * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. + */ + @Nullable + public static RtNetlinkRouteMessage parse(@NonNull final StructNlMsgHdr header, + @NonNull final ByteBuffer byteBuffer) { + final RtNetlinkRouteMessage routeMsg = new RtNetlinkRouteMessage(header); + + routeMsg.mRtmsg = StructRtMsg.parse(byteBuffer); + if (routeMsg.mRtmsg == null) return null; + int rtmFamily = routeMsg.mRtmsg.family; + + // RTA_DST + final int baseOffset = byteBuffer.position(); + StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(RTA_DST, byteBuffer); + if (nlAttr != null) { + final InetAddress destination = nlAttr.getValueAsInetAddress(); + // If the RTA_DST attribute is malformed, return null. + if (destination == null) return null; + // If the address family of destination doesn't match rtm_family, return null. + if (!matchRouteAddressFamily(destination, rtmFamily)) return null; + routeMsg.mDestination = new IpPrefix(destination, routeMsg.mRtmsg.dstLen); + } else if (rtmFamily == AF_INET) { + routeMsg.mDestination = new IpPrefix(IPV4_ADDR_ANY, 0); + } else if (rtmFamily == AF_INET6) { + routeMsg.mDestination = new IpPrefix(IPV6_ADDR_ANY, 0); + } else { + return null; + } + + // RTA_GATEWAY + byteBuffer.position(baseOffset); + nlAttr = StructNlAttr.findNextAttrOfType(RTA_GATEWAY, byteBuffer); + if (nlAttr != null) { + routeMsg.mGateway = nlAttr.getValueAsInetAddress(); + // If the RTA_GATEWAY attribute is malformed, return null. + if (routeMsg.mGateway == null) return null; + // If the address family of gateway doesn't match rtm_family, return null. + if (!matchRouteAddressFamily(routeMsg.mGateway, rtmFamily)) return null; + } + + // RTA_OIF + byteBuffer.position(baseOffset); + nlAttr = StructNlAttr.findNextAttrOfType(RTA_OIF, byteBuffer); + if (nlAttr != null) { + // Any callers that deal with interface names are responsible for converting + // the interface index to a name themselves. This may not succeed or may be + // incorrect, because the interface might have been deleted, or even deleted + // and re-added with a different index, since the netlink message was sent. + routeMsg.mIfindex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */); + } + + return routeMsg; + } + + /** + * Write a rtnetlink address message to {@link ByteBuffer}. + */ + @VisibleForTesting + protected void pack(ByteBuffer byteBuffer) { + getHeader().pack(byteBuffer); + mRtmsg.pack(byteBuffer); + + final StructNlAttr destination = new StructNlAttr(RTA_DST, mDestination.getAddress()); + destination.pack(byteBuffer); + + if (mGateway != null) { + final StructNlAttr gateway = new StructNlAttr(RTA_GATEWAY, mGateway.getAddress()); + gateway.pack(byteBuffer); + } + if (mIfindex != 0) { + final StructNlAttr ifindex = new StructNlAttr(RTA_OIF, mIfindex); + ifindex.pack(byteBuffer); + } + } + + @Override + public String toString() { + return "RtNetlinkRouteMessage{ " + + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, " + + "Rtmsg{" + mRtmsg.toString() + "}, " + + "destination{" + mDestination.getAddress().getHostAddress() + "}, " + + "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, " + + "ifindex{" + mIfindex + "} " + + "}"; + } +} diff --git a/common/device/com/android/net/module/util/netlink/StructRtMsg.java b/common/device/com/android/net/module/util/netlink/StructRtMsg.java new file mode 100644 index 00000000..3cd72922 --- /dev/null +++ b/common/device/com/android/net/module/util/netlink/StructRtMsg.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 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 com.android.net.module.util.netlink; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + +import java.nio.ByteBuffer; + +/** + * struct rtmsg + * + * see also: + * + * include/uapi/linux/rtnetlink.h + * + * @hide + */ +public class StructRtMsg extends Struct { + // Already aligned. + public static final int STRUCT_SIZE = 12; + + @Field(order = 0, type = Type.U8) + public final short family; // Address family of route. + @Field(order = 1, type = Type.U8) + public final short dstLen; // Length of destination. + @Field(order = 2, type = Type.U8) + public final short srcLen; // Length of source. + @Field(order = 3, type = Type.U8) + public final short tos; // TOS filter. + @Field(order = 4, type = Type.U8) + public final short table; // Routing table ID. + @Field(order = 5, type = Type.U8) + public final short protocol; // Routing protocol. + @Field(order = 6, type = Type.U8) + public final short scope; // distance to the destination. + @Field(order = 7, type = Type.U8) + public final short type; // route type + @Field(order = 8, type = Type.U32) + public final long flags; + + StructRtMsg(short family, short dstLen, short srcLen, short tos, short table, short protocol, + short scope, short type, long flags) { + this.family = family; + this.dstLen = dstLen; + this.srcLen = srcLen; + this.tos = tos; + this.table = table; + this.protocol = protocol; + this.scope = scope; + this.type = type; + this.flags = flags; + } + + /** + * Parse a rtmsg struct from a {@link ByteBuffer}. + * + * @param byteBuffer The buffer from which to parse the rtmsg struct. + * @return the parsed rtmsg struct, or {@code null} if the rtmsg struct could not be + * parsed successfully (for example, if it was truncated). + */ + @Nullable + public static StructRtMsg parse(@NonNull final ByteBuffer byteBuffer) { + if (byteBuffer.remaining() < STRUCT_SIZE) return null; + + // The ByteOrder must already have been set to native order. + return Struct.parse(StructRtMsg.class, byteBuffer); + } + + /** + * Write the rtmsg struct to {@link ByteBuffer}. + */ + public void pack(@NonNull final ByteBuffer byteBuffer) { + // The ByteOrder must already have been set to native order. + this.writeToByteBuffer(byteBuffer); + } +} |