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_UNSPEC; 20 21 import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; 22 import static com.android.net.module.util.netlink.NetlinkConstants.IFF_UP; 23 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_GETLINK; 24 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWLINK; 25 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST_ACK; 26 27 import android.net.MacAddress; 28 import android.system.OsConstants; 29 30 import androidx.annotation.NonNull; 31 import androidx.annotation.Nullable; 32 import androidx.annotation.VisibleForTesting; 33 34 import java.nio.ByteBuffer; 35 import java.nio.ByteOrder; 36 37 /** 38 * A NetlinkMessage subclass for rtnetlink link messages. 39 * 40 * RtNetlinkLinkMessage.parse() must be called with a ByteBuffer that contains exactly one netlink 41 * message. 42 * 43 * see also: 44 * 45 * include/uapi/linux/rtnetlink.h 46 * 47 * @hide 48 */ 49 public class RtNetlinkLinkMessage extends NetlinkMessage { 50 public static final short IFLA_ADDRESS = 1; 51 public static final short IFLA_IFNAME = 3; 52 public static final short IFLA_MTU = 4; 53 public static final short IFLA_INET6_ADDR_GEN_MODE = 8; 54 public static final short IFLA_AF_SPEC = 26; 55 56 public static final short IN6_ADDR_GEN_MODE_NONE = 1; 57 58 // The maximum buffer size to hold an interface name including the null-terminator '\0'. 59 private static final int IFNAMSIZ = 16; 60 // The default value of MTU, which means the MTU is unspecified. 61 private static final int DEFAULT_MTU = 0; 62 63 @NonNull 64 private final StructIfinfoMsg mIfinfomsg; 65 private final int mMtu; 66 @Nullable 67 private final MacAddress mHardwareAddress; 68 @Nullable 69 private final String mInterfaceName; 70 71 /** 72 * Creates an {@link RtNetlinkLinkMessage} instance. 73 * 74 * <p>This method validates the arguments and returns {@code null} if any of them are invalid. 75 * nlmsghdr's nlmsg_len will be updated to the correct length before creation. 76 * 77 * @param nlmsghdr The Netlink message header. Must not be {@code null}. 78 * @param ifinfomsg The interface information message. Must not be {@code null}. 79 * @param mtu The Maximum Transmission Unit (MTU) value for the link. 80 * @param hardwareAddress The hardware address (MAC address) of the link. May be {@code null}. 81 * @param interfaceName The name of the interface. May be {@code null}. 82 * @return A new {@link RtNetlinkLinkMessage} instance, or {@code null} if the input arguments 83 * are invalid. 84 */ 85 @Nullable build(@onNull StructNlMsgHdr nlmsghdr, @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, @Nullable String interfaceName)86 public static RtNetlinkLinkMessage build(@NonNull StructNlMsgHdr nlmsghdr, 87 @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, 88 @Nullable String interfaceName) { 89 if (mtu < 0) { 90 return null; 91 } 92 if (interfaceName != null 93 && (interfaceName.isEmpty() || interfaceName.length() + 1 > IFNAMSIZ)) { 94 return null; 95 } 96 97 nlmsghdr.nlmsg_len = calculateMessageLength(mtu, hardwareAddress, interfaceName); 98 return new RtNetlinkLinkMessage(nlmsghdr, ifinfomsg, mtu, hardwareAddress, interfaceName); 99 } 100 RtNetlinkLinkMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, @Nullable String interfaceName)101 private RtNetlinkLinkMessage(@NonNull StructNlMsgHdr nlmsghdr, 102 @NonNull StructIfinfoMsg ifinfomsg, int mtu, @Nullable MacAddress hardwareAddress, 103 @Nullable String interfaceName) { 104 super(nlmsghdr); 105 mIfinfomsg = ifinfomsg; 106 mMtu = mtu; 107 mHardwareAddress = hardwareAddress; 108 mInterfaceName = interfaceName; 109 } 110 getMtu()111 public int getMtu() { 112 return mMtu; 113 } 114 115 @NonNull getIfinfoHeader()116 public StructIfinfoMsg getIfinfoHeader() { 117 return mIfinfomsg; 118 } 119 120 @Nullable getHardwareAddress()121 public MacAddress getHardwareAddress() { 122 return mHardwareAddress; 123 } 124 125 @Nullable getInterfaceName()126 public String getInterfaceName() { 127 return mInterfaceName; 128 } 129 130 /** 131 * Parse rtnetlink link message from {@link ByteBuffer}. This method must be called with a 132 * ByteBuffer that contains exactly one netlink message. 133 * 134 * @param header netlink message header. 135 * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes. 136 */ 137 @Nullable parse(@onNull final StructNlMsgHdr header, @NonNull final ByteBuffer byteBuffer)138 public static RtNetlinkLinkMessage parse(@NonNull final StructNlMsgHdr header, 139 @NonNull final ByteBuffer byteBuffer) { 140 final StructIfinfoMsg ifinfoMsg = StructIfinfoMsg.parse(byteBuffer); 141 if (ifinfoMsg == null) { 142 return null; 143 } 144 145 // IFLA_MTU 146 int mtu = DEFAULT_MTU; 147 final int baseOffset = byteBuffer.position(); 148 StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(IFLA_MTU, byteBuffer); 149 if (nlAttr != null) { 150 mtu = nlAttr.getValueAsInt(DEFAULT_MTU); 151 } 152 153 // IFLA_ADDRESS 154 MacAddress hardwareAddress = null; 155 byteBuffer.position(baseOffset); 156 nlAttr = StructNlAttr.findNextAttrOfType(IFLA_ADDRESS, byteBuffer); 157 if (nlAttr != null) { 158 hardwareAddress = nlAttr.getValueAsMacAddress(); 159 } 160 161 // IFLA_IFNAME 162 String interfaceName = null; 163 byteBuffer.position(baseOffset); 164 nlAttr = StructNlAttr.findNextAttrOfType(IFLA_IFNAME, byteBuffer); 165 if (nlAttr != null) { 166 interfaceName = nlAttr.getValueAsString(); 167 } 168 169 return new RtNetlinkLinkMessage(header, ifinfoMsg, mtu, hardwareAddress, interfaceName); 170 } 171 172 /** 173 * Write a rtnetlink link message to {@link byte} array. 174 */ pack(ByteOrder order)175 public byte[] pack(ByteOrder order) { 176 byte[] bytes = new byte[mHeader.nlmsg_len]; 177 ByteBuffer buffer = ByteBuffer.wrap(bytes).order(order); 178 pack(buffer); 179 return bytes; 180 } 181 182 /** 183 * Write a rtnetlink link message to {@link ByteBuffer}. 184 */ 185 @VisibleForTesting pack(ByteBuffer byteBuffer)186 protected void pack(ByteBuffer byteBuffer) { 187 mHeader.pack(byteBuffer); 188 mIfinfomsg.pack(byteBuffer); 189 190 if (mMtu != DEFAULT_MTU) { 191 final StructNlAttr mtu = new StructNlAttr(IFLA_MTU, mMtu); 192 mtu.pack(byteBuffer); 193 } 194 if (mHardwareAddress != null) { 195 final StructNlAttr hardwareAddress = new StructNlAttr(IFLA_ADDRESS, mHardwareAddress); 196 hardwareAddress.pack(byteBuffer); 197 } 198 if (mInterfaceName != null) { 199 final StructNlAttr ifname = new StructNlAttr(IFLA_IFNAME, mInterfaceName); 200 ifname.pack(byteBuffer); 201 } 202 } 203 204 /** 205 * Calculate the byte length of the packed buffer. 206 */ calculateMessageLength(int mtu, MacAddress hardwareAddress, String interfaceName)207 private static int calculateMessageLength(int mtu, MacAddress hardwareAddress, 208 String interfaceName) { 209 int length = StructNlMsgHdr.STRUCT_SIZE + StructIfinfoMsg.STRUCT_SIZE; 210 211 if (mtu != DEFAULT_MTU) { 212 length += NetlinkConstants.alignedLengthOf(StructNlAttr.NLA_HEADERLEN + Integer.BYTES); 213 } 214 if (hardwareAddress != null) { 215 length += NetlinkConstants.alignedLengthOf( 216 StructNlAttr.NLA_HEADERLEN + ETHER_ADDR_LEN); 217 } 218 if (interfaceName != null) { 219 length += NetlinkConstants.alignedLengthOf( 220 // The string should be end with '\0', so the length should plus 1. 221 StructNlAttr.NLA_HEADERLEN + interfaceName.length() + 1); 222 } 223 224 return length; 225 } 226 227 /** 228 * Create a link message to set the operational state (up or down) of a network interface. 229 * 230 * @param interfaceName The network interface name. 231 * @param sequenceNumber The sequence number to use for the Netlink message. 232 * @param isUp {@code true} to set the interface up, {@code false} to set it down. 233 * @return A `RtNetlinkLinkMessage` instance configured to set the link state. 234 */ 235 @Nullable createSetLinkStateMessage(@onNull String interfaceName, int sequenceNumber, boolean isUp)236 public static RtNetlinkLinkMessage createSetLinkStateMessage(@NonNull String interfaceName, 237 int sequenceNumber, boolean isUp) { 238 return createSetLinkStateMessage(interfaceName, sequenceNumber, isUp, new OsAccess()); 239 } 240 241 @VisibleForTesting 242 @Nullable createSetLinkStateMessage(@onNull String interfaceName, int sequenceNumber, boolean isUp, OsAccess osAccess)243 protected static RtNetlinkLinkMessage createSetLinkStateMessage(@NonNull String interfaceName, 244 int sequenceNumber, boolean isUp, OsAccess osAccess) { 245 final int interfaceIndex = osAccess.if_nametoindex(interfaceName); 246 if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) { 247 return null; 248 } 249 250 return RtNetlinkLinkMessage.build( 251 new StructNlMsgHdr(0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber), 252 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex, 253 isUp ? IFF_UP : 0, IFF_UP), DEFAULT_MTU, null, null); 254 } 255 256 /** 257 * Create a link message to rename the network interface. 258 * 259 * @param interfaceName The network interface name. 260 * @param sequenceNumber The sequence number to use for the Netlink message. 261 * @param newName The new name of the network interface. 262 * @return A `RtNetlinkLinkMessage` instance configured to rename the network interface. 263 */ 264 @Nullable createSetLinkNameMessage(@onNull String interfaceName, int sequenceNumber, @NonNull String newName)265 public static RtNetlinkLinkMessage createSetLinkNameMessage(@NonNull String interfaceName, 266 int sequenceNumber, @NonNull String newName) { 267 return createSetLinkNameMessage(interfaceName, sequenceNumber, newName, new OsAccess()); 268 } 269 270 @VisibleForTesting 271 @Nullable createSetLinkNameMessage(@onNull String interfaceName, int sequenceNumber, @NonNull String newName, OsAccess osAccess)272 protected static RtNetlinkLinkMessage createSetLinkNameMessage(@NonNull String interfaceName, 273 int sequenceNumber, @NonNull String newName, OsAccess osAccess) { 274 final int interfaceIndex = osAccess.if_nametoindex(interfaceName); 275 if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) { 276 return null; 277 } 278 279 return RtNetlinkLinkMessage.build( 280 new StructNlMsgHdr(0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber), 281 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex, 0, 0), 282 DEFAULT_MTU, null, newName); 283 } 284 285 /** 286 * Creates an {@link RtNetlinkLinkMessage} instance that can be used to get the link information 287 * of a network interface. 288 * 289 * @param interfaceName The name of the network interface to query. 290 * @param sequenceNumber The sequence number for the Netlink message. 291 * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface. 292 */ 293 @Nullable createGetLinkMessage(@onNull String interfaceName, int sequenceNumber)294 public static RtNetlinkLinkMessage createGetLinkMessage(@NonNull String interfaceName, 295 int sequenceNumber) { 296 return createGetLinkMessage(interfaceName, sequenceNumber, new OsAccess()); 297 } 298 299 @VisibleForTesting 300 @Nullable createGetLinkMessage(@onNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess)301 protected static RtNetlinkLinkMessage createGetLinkMessage(@NonNull String interfaceName, 302 int sequenceNumber, @NonNull OsAccess osAccess) { 303 final int interfaceIndex = osAccess.if_nametoindex(interfaceName); 304 if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) { 305 return null; 306 } 307 308 return RtNetlinkLinkMessage.build( 309 new StructNlMsgHdr(0, RTM_GETLINK, NLM_F_REQUEST_ACK, sequenceNumber), 310 new StructIfinfoMsg((short) AF_UNSPEC, (short) 0, interfaceIndex, 0, 0), 311 DEFAULT_MTU, null, null); 312 } 313 314 /** 315 * Creates an {@link RtNetlinkLinkMessage} instance that can be used to set the flags of a 316 * network interface. 317 * 318 * @param interfaceName The name of the network interface to query. 319 * @param sequenceNumber The sequence number for the Netlink message. 320 * @param flags power-of-two integer flags to set or unset. A flag to set should be passed as 321 * is as a power-of-two value, and a flag to remove should be passed inversed as -1 with 322 * a single bit down. For example: IFF_UP, ~IFF_BROADCAST... 323 * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface. 324 */ 325 @Nullable createSetFlagsMessage(@onNull String interfaceName, int sequenceNumber, int... flags)326 public static RtNetlinkLinkMessage createSetFlagsMessage(@NonNull String interfaceName, 327 int sequenceNumber, int... flags) { 328 return createSetFlagsMessage( 329 interfaceName, sequenceNumber, new OsAccess(), flags); 330 } 331 332 @VisibleForTesting 333 @Nullable createSetFlagsMessage( @onNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess, int... flags)334 protected static RtNetlinkLinkMessage createSetFlagsMessage( 335 @NonNull String interfaceName, int sequenceNumber, @NonNull OsAccess osAccess, 336 int... flags) { 337 final int interfaceIndex = osAccess.if_nametoindex(interfaceName); 338 if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) { 339 return null; 340 } 341 342 int flagsBits = 0; 343 int changeBits = 0; 344 for (int f : flags) { 345 if (Integer.bitCount(f) == 1) { 346 flagsBits |= f; 347 changeBits |= f; 348 } else if (Integer.bitCount(~f) == 1) { 349 flagsBits &= f; 350 changeBits |= ~f; 351 } else { 352 return null; 353 } 354 } 355 // RTM_NEWLINK is used here for create, modify, or notify changes about a internet 356 // interface, including change in administrative state. While RTM_SETLINK is used to 357 // modify an existing link rather than creating a new one. 358 return RtNetlinkLinkMessage.build( 359 new StructNlMsgHdr( 360 /*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST_ACK, sequenceNumber), 361 new StructIfinfoMsg((short) AF_UNSPEC, /*type*/ 0, interfaceIndex, 362 flagsBits, changeBits), 363 DEFAULT_MTU, /*hardwareAddress*/ null, /*interfaceName*/ null); 364 } 365 366 /** 367 * Creates an {@link RtNetlinkLinkMessage} instance that can be used to set the MTU of a 368 * network interface. 369 * 370 * @param interfaceName The name of the network interface to query. 371 * @param sequenceNumber The sequence number for the Netlink message. 372 * @param mtu MTU value to set for the interface. 373 * @return An `RtNetlinkLinkMessage` instance representing the request to query the interface. 374 */ 375 @Nullable createSetMtuMessage(@onNull String interfaceName, int sequenceNumber, int mtu)376 public static RtNetlinkLinkMessage createSetMtuMessage(@NonNull String interfaceName, 377 int sequenceNumber, int mtu) { 378 return createSetMtuMessage( 379 interfaceName, sequenceNumber, mtu, new OsAccess()); 380 } 381 382 @VisibleForTesting 383 @Nullable createSetMtuMessage(@onNull String interfaceName, int sequenceNumber, int mtu, @NonNull OsAccess osAccess)384 protected static RtNetlinkLinkMessage createSetMtuMessage(@NonNull String interfaceName, 385 int sequenceNumber, int mtu, @NonNull OsAccess osAccess) { 386 final int interfaceIndex = osAccess.if_nametoindex(interfaceName); 387 if (interfaceIndex == OsAccess.INVALID_INTERFACE_INDEX) { 388 return null; 389 } 390 return RtNetlinkLinkMessage.build( 391 new StructNlMsgHdr(/*payloadLen*/ 0, RTM_NEWLINK, NLM_F_REQUEST_ACK , sequenceNumber), 392 new StructIfinfoMsg((short) AF_UNSPEC, /*type*/ 0, interfaceIndex, 393 /*flags*/ 0, /*change*/ 0), 394 mtu, /*hardwareAddress*/ null, /*interfaceName*/ null); 395 } 396 397 @Override toString()398 public String toString() { 399 return "RtNetlinkLinkMessage{ " 400 + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, " 401 + "Ifinfomsg{" + mIfinfomsg + "}, " 402 + "Hardware Address{" + mHardwareAddress + "}, " 403 + "MTU{" + mMtu + "}, " 404 + "Ifname{" + mInterfaceName + "} " 405 + "}"; 406 } 407 } 408