1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.net.module.util.netlink; 18 19 import static android.system.OsConstants.AF_INET; 20 import static android.system.OsConstants.AF_INET6; 21 22 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY; 23 import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY; 24 25 import android.annotation.SuppressLint; 26 import android.net.IpPrefix; 27 import android.system.OsConstants; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 import androidx.annotation.VisibleForTesting; 32 33 import java.net.Inet4Address; 34 import java.net.Inet6Address; 35 import java.net.InetAddress; 36 import java.nio.ByteBuffer; 37 38 /** 39 * A NetlinkMessage subclass for rtnetlink route messages. 40 * 41 * RtNetlinkRouteMessage.parse() must be called with a ByteBuffer that contains exactly one 42 * netlink message. 43 * 44 * see also: 45 * 46 * include/uapi/linux/rtnetlink.h 47 * 48 * @hide 49 */ 50 public class RtNetlinkRouteMessage extends NetlinkMessage { 51 public static final short RTA_DST = 1; 52 public static final short RTA_OIF = 4; 53 public static final short RTA_GATEWAY = 5; 54 55 private int mIfindex; 56 @NonNull 57 private StructRtMsg mRtmsg; 58 @NonNull 59 private IpPrefix mDestination; 60 @Nullable 61 private InetAddress mGateway; 62 RtNetlinkRouteMessage(StructNlMsgHdr header)63 private RtNetlinkRouteMessage(StructNlMsgHdr header) { 64 super(header); 65 mRtmsg = null; 66 mDestination = null; 67 mGateway = null; 68 mIfindex = 0; 69 } 70 getInterfaceIndex()71 public int getInterfaceIndex() { 72 return mIfindex; 73 } 74 75 @NonNull getRtMsgHeader()76 public StructRtMsg getRtMsgHeader() { 77 return mRtmsg; 78 } 79 80 @NonNull getDestination()81 public IpPrefix getDestination() { 82 return mDestination; 83 } 84 85 @Nullable getGateway()86 public InetAddress getGateway() { 87 return mGateway; 88 } 89 90 /** 91 * Check whether the address families of destination and gateway match rtm_family in 92 * StructRtmsg. 93 * 94 * For example, IPv4-mapped IPv6 addresses as an IPv6 address will be always converted to IPv4 95 * address, that's incorrect when upper layer creates a new {@link RouteInfo} class instance 96 * for IPv6 route with the converted IPv4 gateway. 97 */ matchRouteAddressFamily(@onNull final InetAddress address, int family)98 private static boolean matchRouteAddressFamily(@NonNull final InetAddress address, 99 int family) { 100 return ((address instanceof Inet4Address) && (family == AF_INET)) 101 || ((address instanceof Inet6Address) && (family == AF_INET6)); 102 } 103 104 /** 105 * Parse rtnetlink route message from {@link ByteBuffer}. This method must be called with a 106 * ByteBuffer that contains exactly one netlink message. 107 * 108 * @param header netlink message header. 109 * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. 110 */ 111 @SuppressLint("NewApi") 112 @Nullable parse(@onNull final StructNlMsgHdr header, @NonNull final ByteBuffer byteBuffer)113 public static RtNetlinkRouteMessage parse(@NonNull final StructNlMsgHdr header, 114 @NonNull final ByteBuffer byteBuffer) { 115 final RtNetlinkRouteMessage routeMsg = new RtNetlinkRouteMessage(header); 116 117 routeMsg.mRtmsg = StructRtMsg.parse(byteBuffer); 118 if (routeMsg.mRtmsg == null) return null; 119 int rtmFamily = routeMsg.mRtmsg.family; 120 121 // RTA_DST 122 final int baseOffset = byteBuffer.position(); 123 StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(RTA_DST, byteBuffer); 124 if (nlAttr != null) { 125 final InetAddress destination = nlAttr.getValueAsInetAddress(); 126 // If the RTA_DST attribute is malformed, return null. 127 if (destination == null) return null; 128 // If the address family of destination doesn't match rtm_family, return null. 129 if (!matchRouteAddressFamily(destination, rtmFamily)) return null; 130 routeMsg.mDestination = new IpPrefix(destination, routeMsg.mRtmsg.dstLen); 131 } else if (rtmFamily == AF_INET) { 132 routeMsg.mDestination = new IpPrefix(IPV4_ADDR_ANY, 0); 133 } else if (rtmFamily == AF_INET6) { 134 routeMsg.mDestination = new IpPrefix(IPV6_ADDR_ANY, 0); 135 } else { 136 return null; 137 } 138 139 // RTA_GATEWAY 140 byteBuffer.position(baseOffset); 141 nlAttr = StructNlAttr.findNextAttrOfType(RTA_GATEWAY, byteBuffer); 142 if (nlAttr != null) { 143 routeMsg.mGateway = nlAttr.getValueAsInetAddress(); 144 // If the RTA_GATEWAY attribute is malformed, return null. 145 if (routeMsg.mGateway == null) return null; 146 // If the address family of gateway doesn't match rtm_family, return null. 147 if (!matchRouteAddressFamily(routeMsg.mGateway, rtmFamily)) return null; 148 } 149 150 // RTA_OIF 151 byteBuffer.position(baseOffset); 152 nlAttr = StructNlAttr.findNextAttrOfType(RTA_OIF, byteBuffer); 153 if (nlAttr != null) { 154 // Any callers that deal with interface names are responsible for converting 155 // the interface index to a name themselves. This may not succeed or may be 156 // incorrect, because the interface might have been deleted, or even deleted 157 // and re-added with a different index, since the netlink message was sent. 158 routeMsg.mIfindex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */); 159 } 160 161 return routeMsg; 162 } 163 164 /** 165 * Write a rtnetlink address message to {@link ByteBuffer}. 166 */ 167 @VisibleForTesting pack(ByteBuffer byteBuffer)168 protected void pack(ByteBuffer byteBuffer) { 169 getHeader().pack(byteBuffer); 170 mRtmsg.pack(byteBuffer); 171 172 final StructNlAttr destination = new StructNlAttr(RTA_DST, mDestination.getAddress()); 173 destination.pack(byteBuffer); 174 175 if (mGateway != null) { 176 final StructNlAttr gateway = new StructNlAttr(RTA_GATEWAY, mGateway.getAddress()); 177 gateway.pack(byteBuffer); 178 } 179 if (mIfindex != 0) { 180 final StructNlAttr ifindex = new StructNlAttr(RTA_OIF, mIfindex); 181 ifindex.pack(byteBuffer); 182 } 183 } 184 185 @Override toString()186 public String toString() { 187 return "RtNetlinkRouteMessage{ " 188 + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, " 189 + "Rtmsg{" + mRtmsg.toString() + "}, " 190 + "destination{" + mDestination.getAddress().getHostAddress() + "}, " 191 + "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, " 192 + "ifindex{" + mIfindex + "} " 193 + "}"; 194 } 195 } 196