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 com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress; 20 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK; 21 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE; 22 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; 23 24 import android.system.OsConstants; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.annotation.VisibleForTesting; 29 30 import com.android.net.module.util.HexDump; 31 32 import java.net.Inet4Address; 33 import java.net.Inet6Address; 34 import java.net.InetAddress; 35 import java.nio.ByteBuffer; 36 import java.nio.ByteOrder; 37 import java.util.Objects; 38 39 /** 40 * A NetlinkMessage subclass for rtnetlink address messages. 41 * 42 * RtNetlinkAddressMessage.parse() must be called with a ByteBuffer that contains exactly one 43 * netlink message. 44 * 45 * see also: 46 * 47 * include/uapi/linux/rtnetlink.h 48 * 49 * @hide 50 */ 51 public class RtNetlinkAddressMessage extends NetlinkMessage { 52 public static final short IFA_ADDRESS = 1; 53 public static final short IFA_LOCAL = 2; 54 public static final short IFA_BROADCAST = 4; 55 public static final short IFA_CACHEINFO = 6; 56 public static final short IFA_FLAGS = 8; 57 58 private int mFlags; 59 @NonNull 60 private StructIfaddrMsg mIfaddrmsg; 61 @NonNull 62 private InetAddress mIpAddress; 63 @Nullable 64 private StructIfacacheInfo mIfacacheInfo; 65 66 @VisibleForTesting RtNetlinkAddressMessage(@onNull final StructNlMsgHdr header, @NonNull final StructIfaddrMsg ifaddrMsg, @NonNull final InetAddress ipAddress, @Nullable final StructIfacacheInfo structIfacacheInfo, int flags)67 public RtNetlinkAddressMessage(@NonNull final StructNlMsgHdr header, 68 @NonNull final StructIfaddrMsg ifaddrMsg, 69 @NonNull final InetAddress ipAddress, 70 @Nullable final StructIfacacheInfo structIfacacheInfo, 71 int flags) { 72 super(header); 73 mIfaddrmsg = ifaddrMsg; 74 mIpAddress = ipAddress; 75 mIfacacheInfo = structIfacacheInfo; 76 mFlags = flags; 77 } 78 RtNetlinkAddressMessage(@onNull StructNlMsgHdr header)79 private RtNetlinkAddressMessage(@NonNull StructNlMsgHdr header) { 80 this(header, null, null, null, 0); 81 } 82 getFlags()83 public int getFlags() { 84 return mFlags; 85 } 86 87 @NonNull getIfaddrHeader()88 public StructIfaddrMsg getIfaddrHeader() { 89 return mIfaddrmsg; 90 } 91 92 @NonNull getIpAddress()93 public InetAddress getIpAddress() { 94 return mIpAddress; 95 } 96 97 @Nullable getIfacacheInfo()98 public StructIfacacheInfo getIfacacheInfo() { 99 return mIfacacheInfo; 100 } 101 102 /** 103 * Parse rtnetlink address message from {@link ByteBuffer}. This method must be called with a 104 * ByteBuffer that contains exactly one netlink message. 105 * 106 * @param header netlink message header. 107 * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. 108 */ 109 @Nullable parse(@onNull final StructNlMsgHdr header, @NonNull final ByteBuffer byteBuffer)110 public static RtNetlinkAddressMessage parse(@NonNull final StructNlMsgHdr header, 111 @NonNull final ByteBuffer byteBuffer) { 112 final RtNetlinkAddressMessage addrMsg = new RtNetlinkAddressMessage(header); 113 114 addrMsg.mIfaddrmsg = StructIfaddrMsg.parse(byteBuffer); 115 if (addrMsg.mIfaddrmsg == null) return null; 116 117 // IFA_ADDRESS 118 final int baseOffset = byteBuffer.position(); 119 StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(IFA_ADDRESS, byteBuffer); 120 if (nlAttr == null) return null; 121 addrMsg.mIpAddress = nlAttr.getValueAsInetAddress(); 122 if (addrMsg.mIpAddress == null) return null; 123 124 // IFA_CACHEINFO 125 byteBuffer.position(baseOffset); 126 nlAttr = StructNlAttr.findNextAttrOfType(IFA_CACHEINFO, byteBuffer); 127 if (nlAttr != null) { 128 addrMsg.mIfacacheInfo = StructIfacacheInfo.parse(nlAttr.getValueAsByteBuffer()); 129 } 130 131 // The first 8 bits of flags are in the ifaddrmsg. 132 addrMsg.mFlags = addrMsg.mIfaddrmsg.flags; 133 // IFA_FLAGS. All the flags are in the IF_FLAGS attribute. This should always be present, 134 // and will overwrite the flags set above. 135 byteBuffer.position(baseOffset); 136 nlAttr = StructNlAttr.findNextAttrOfType(IFA_FLAGS, byteBuffer); 137 if (nlAttr == null) return null; 138 final Integer value = nlAttr.getValueAsInteger(); 139 if (value == null) return null; 140 addrMsg.mFlags = value; 141 142 return addrMsg; 143 } 144 145 /** 146 * Write a rtnetlink address message to {@link ByteBuffer}. 147 */ 148 @VisibleForTesting pack(ByteBuffer byteBuffer)149 protected void pack(ByteBuffer byteBuffer) { 150 getHeader().pack(byteBuffer); 151 mIfaddrmsg.pack(byteBuffer); 152 153 final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, mIpAddress); 154 address.pack(byteBuffer); 155 156 if (mIfacacheInfo != null) { 157 final StructNlAttr cacheInfo = new StructNlAttr(IFA_CACHEINFO, 158 mIfacacheInfo.writeToBytes()); 159 cacheInfo.pack(byteBuffer); 160 } 161 162 // If IFA_FLAGS attribute isn't present on the wire at parsing netlink message, it will 163 // still be packed to ByteBuffer even if the flag is 0. 164 final StructNlAttr flags = new StructNlAttr(IFA_FLAGS, mFlags); 165 flags.pack(byteBuffer); 166 167 // Add the required IFA_LOCAL and IFA_BROADCAST attributes for IPv4 addresses. The IFA_LOCAL 168 // attribute represents the local address, which is equivalent to IFA_ADDRESS on a normally 169 // configured broadcast interface, however, for PPP interfaces, IFA_ADDRESS indicates the 170 // destination address and the local address is provided in the IFA_LOCAL attribute. If the 171 // IFA_LOCAL attribute is not present in the RTM_NEWADDR message, the kernel replies with an 172 // error netlink message with invalid parameters. IFA_BROADCAST is also required, otherwise 173 // the broadcast on the interface is 0.0.0.0. See include/uapi/linux/if_addr.h for details. 174 // For IPv6 addresses, the IFA_ADDRESS attribute applies and introduces no ambiguity. 175 if (mIpAddress instanceof Inet4Address) { 176 final StructNlAttr localAddress = new StructNlAttr(IFA_LOCAL, mIpAddress); 177 localAddress.pack(byteBuffer); 178 179 final Inet4Address broadcast = 180 getBroadcastAddress((Inet4Address) mIpAddress, mIfaddrmsg.prefixLen); 181 final StructNlAttr broadcastAddress = new StructNlAttr(IFA_BROADCAST, broadcast); 182 broadcastAddress.pack(byteBuffer); 183 } 184 } 185 186 /** 187 * A convenience method to create a RTM_NEWADDR message. 188 */ newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip, short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid)189 public static byte[] newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip, 190 short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid) { 191 Objects.requireNonNull(ip, "IP address to be set via netlink message cannot be null"); 192 193 final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); 194 nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWADDR; 195 nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; 196 nlmsghdr.nlmsg_seq = seqNo; 197 198 final RtNetlinkAddressMessage msg = new RtNetlinkAddressMessage(nlmsghdr); 199 final byte family = 200 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET); 201 // IFA_FLAGS attribute is always present within this method, just set flags from 202 // ifaddrmsg to 0. kernel will prefer the flags from IFA_FLAGS attribute. 203 msg.mIfaddrmsg = 204 new StructIfaddrMsg(family, prefixlen, (short) 0 /* flags */, scope, ifIndex); 205 msg.mIpAddress = ip; 206 msg.mIfacacheInfo = new StructIfacacheInfo(preferred, valid, 0 /* cstamp */, 207 0 /* tstamp */); 208 msg.mFlags = flags; 209 210 final byte[] bytes = new byte[msg.getRequiredSpace(family)]; 211 nlmsghdr.nlmsg_len = bytes.length; 212 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 213 byteBuffer.order(ByteOrder.nativeOrder()); 214 msg.pack(byteBuffer); 215 return bytes; 216 } 217 218 /** 219 * A convenience method to create a RTM_DELADDR message. 220 */ newRtmDelAddressMessage(int seqNo, @NonNull final InetAddress ip, short prefixlen, int ifIndex)221 public static byte[] newRtmDelAddressMessage(int seqNo, @NonNull final InetAddress ip, 222 short prefixlen, int ifIndex) { 223 Objects.requireNonNull(ip, "IP address to be deleted via netlink message cannot be null"); 224 225 final int ifaAddrAttrLength = NetlinkConstants.alignedLengthOf( 226 StructNlAttr.NLA_HEADERLEN + ip.getAddress().length); 227 final int length = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE 228 + ifaAddrAttrLength; 229 final byte[] bytes = new byte[length]; 230 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 231 byteBuffer.order(ByteOrder.nativeOrder()); 232 233 final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); 234 nlmsghdr.nlmsg_len = length; 235 nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR; 236 nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 237 nlmsghdr.nlmsg_seq = seqNo; 238 nlmsghdr.pack(byteBuffer); 239 240 final byte family = 241 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET); 242 // Actually kernel ignores scope and flags(only deal with IFA_F_MANAGETEMPADDR, it 243 // indicates that all relevant IPv6 temporary addresses should be deleted as well when 244 // user space intends to delete a global IPv6 address with IFA_F_MANAGETEMPADDR), so 245 // far IFA_F_MANAGETEMPADDR flag isn't used in user space, it's fine to ignore it. 246 // However, we need to add IFA_FLAGS attribute in RTM_DELADDR if flags parsing should 247 // be supported in the future. 248 final StructIfaddrMsg ifaddrmsg = new StructIfaddrMsg(family, prefixlen, 249 (short) 0 /* flags */, (short) 0 /* scope */, ifIndex); 250 ifaddrmsg.pack(byteBuffer); 251 252 final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, ip); 253 address.pack(byteBuffer); 254 255 return bytes; 256 } 257 258 // This function helper gives the required buffer size for IFA_ADDRESS, IFA_CACHEINFO and 259 // IFA_FLAGS attributes encapsulation. However, that's not a mandatory requirement for all 260 // RtNetlinkAddressMessage, e.g. RTM_DELADDR sent from user space to kernel to delete an 261 // IP address only requires IFA_ADDRESS attribute. The caller should check if these attributes 262 // are necessary to carry when constructing a RtNetlinkAddressMessage. getRequiredSpace(int family)263 private int getRequiredSpace(int family) { 264 int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE; 265 // IFA_ADDRESS attr 266 spaceRequired += NetlinkConstants.alignedLengthOf( 267 StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length); 268 // IFA_CACHEINFO attr 269 spaceRequired += NetlinkConstants.alignedLengthOf( 270 StructNlAttr.NLA_HEADERLEN + StructIfacacheInfo.STRUCT_SIZE); 271 // IFA_FLAGS "u32" attr 272 spaceRequired += StructNlAttr.NLA_HEADERLEN + 4; 273 if (family == OsConstants.AF_INET) { 274 // IFA_LOCAL attr 275 spaceRequired += NetlinkConstants.alignedLengthOf( 276 StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length); 277 // IFA_BROADCAST attr 278 spaceRequired += NetlinkConstants.alignedLengthOf( 279 StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length); 280 } 281 return spaceRequired; 282 } 283 284 @Override toString()285 public String toString() { 286 return "RtNetlinkAddressMessage{ " 287 + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, " 288 + "Ifaddrmsg{" + mIfaddrmsg.toString() + "}, " 289 + "IP Address{" + mIpAddress.getHostAddress() + "}, " 290 + "IfacacheInfo{" + (mIfacacheInfo == null ? "" : mIfacacheInfo.toString()) + "}, " 291 + "Address Flags{" + HexDump.toHexString(mFlags) + "} " 292 + "}"; 293 } 294 } 295