1 /* 2 * Copyright (C) 2019 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 android.net.MacAddress; 20 21 import androidx.annotation.NonNull; 22 import androidx.annotation.Nullable; 23 24 import java.io.UnsupportedEncodingException; 25 import java.net.InetAddress; 26 import java.net.UnknownHostException; 27 import java.nio.ByteBuffer; 28 import java.nio.ByteOrder; 29 import java.util.Arrays; 30 31 /** 32 * struct nlattr 33 * 34 * see: <linux_src>/include/uapi/linux/netlink.h 35 * 36 * @hide 37 */ 38 public class StructNlAttr { 39 // Already aligned. 40 public static final int NLA_HEADERLEN = 4; 41 public static final int NLA_F_NESTED = (1 << 15); 42 43 /** 44 * Set carries nested attributes bit. 45 */ makeNestedType(short type)46 public static short makeNestedType(short type) { 47 return (short) (type | NLA_F_NESTED); 48 } 49 50 /** 51 * Peek and parse the netlink attribute from {@link ByteBuffer}. 52 * 53 * Return a (length, type) object only, without consuming any bytes in 54 * |byteBuffer| and without copying or interpreting any value bytes. 55 * This is used for scanning over a packed set of struct nlattr's, 56 * looking for instances of a particular type. 57 */ peek(ByteBuffer byteBuffer)58 public static StructNlAttr peek(ByteBuffer byteBuffer) { 59 if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) { 60 return null; 61 } 62 final int baseOffset = byteBuffer.position(); 63 64 final StructNlAttr struct = new StructNlAttr(); 65 final ByteOrder originalOrder = byteBuffer.order(); 66 byteBuffer.order(ByteOrder.nativeOrder()); 67 try { 68 struct.nla_len = byteBuffer.getShort(); 69 struct.nla_type = byteBuffer.getShort(); 70 } finally { 71 byteBuffer.order(originalOrder); 72 } 73 74 byteBuffer.position(baseOffset); 75 if (struct.nla_len < NLA_HEADERLEN) { 76 // Malformed. 77 return null; 78 } 79 return struct; 80 } 81 82 /** 83 * Parse a netlink attribute from a {@link ByteBuffer}. 84 * 85 * @param byteBuffer The buffer from which to parse the netlink attriute. 86 * @return the parsed netlink attribute, or {@code null} if the netlink attribute 87 * could not be parsed successfully (for example, if it was truncated). 88 */ parse(ByteBuffer byteBuffer)89 public static StructNlAttr parse(ByteBuffer byteBuffer) { 90 final StructNlAttr struct = peek(byteBuffer); 91 if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) { 92 return null; 93 } 94 95 final int baseOffset = byteBuffer.position(); 96 byteBuffer.position(baseOffset + NLA_HEADERLEN); 97 98 int valueLen = ((int) struct.nla_len) & 0xffff; 99 valueLen -= NLA_HEADERLEN; 100 if (valueLen > 0) { 101 struct.nla_value = new byte[valueLen]; 102 byteBuffer.get(struct.nla_value, 0, valueLen); 103 byteBuffer.position(baseOffset + struct.getAlignedLength()); 104 } 105 return struct; 106 } 107 108 /** 109 * Find next netlink attribute with a given type from {@link ByteBuffer}. 110 * 111 * @param attrType The given netlink attribute type is requested for. 112 * @param byteBuffer The buffer from which to find the netlink attribute. 113 * @return the found netlink attribute, or {@code null} if the netlink attribute could not be 114 * found or parsed successfully (for example, if it was truncated). 115 */ 116 @Nullable findNextAttrOfType(short attrType, @Nullable ByteBuffer byteBuffer)117 public static StructNlAttr findNextAttrOfType(short attrType, 118 @Nullable ByteBuffer byteBuffer) { 119 while (byteBuffer != null && byteBuffer.remaining() > 0) { 120 final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer); 121 if (nlAttr == null) { 122 break; 123 } 124 if (nlAttr.nla_type == attrType) { 125 return StructNlAttr.parse(byteBuffer); 126 } 127 if (byteBuffer.remaining() < nlAttr.getAlignedLength()) { 128 break; 129 } 130 byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength()); 131 } 132 return null; 133 } 134 135 public short nla_len = (short) NLA_HEADERLEN; 136 public short nla_type; 137 public byte[] nla_value; 138 StructNlAttr()139 public StructNlAttr() {} 140 StructNlAttr(short type, byte value)141 public StructNlAttr(short type, byte value) { 142 nla_type = type; 143 setValue(new byte[1]); 144 nla_value[0] = value; 145 } 146 StructNlAttr(short type, short value)147 public StructNlAttr(short type, short value) { 148 this(type, value, ByteOrder.nativeOrder()); 149 } 150 StructNlAttr(short type, short value, ByteOrder order)151 public StructNlAttr(short type, short value, ByteOrder order) { 152 nla_type = type; 153 setValue(new byte[Short.BYTES]); 154 final ByteBuffer buf = getValueAsByteBuffer(); 155 final ByteOrder originalOrder = buf.order(); 156 try { 157 buf.order(order); 158 buf.putShort(value); 159 } finally { 160 buf.order(originalOrder); 161 } 162 } 163 StructNlAttr(short type, int value)164 public StructNlAttr(short type, int value) { 165 this(type, value, ByteOrder.nativeOrder()); 166 } 167 StructNlAttr(short type, int value, ByteOrder order)168 public StructNlAttr(short type, int value, ByteOrder order) { 169 nla_type = type; 170 setValue(new byte[Integer.BYTES]); 171 final ByteBuffer buf = getValueAsByteBuffer(); 172 final ByteOrder originalOrder = buf.order(); 173 try { 174 buf.order(order); 175 buf.putInt(value); 176 } finally { 177 buf.order(originalOrder); 178 } 179 } 180 StructNlAttr(short type, @NonNull final byte[] value)181 public StructNlAttr(short type, @NonNull final byte[] value) { 182 nla_type = type; 183 setValue(value); 184 } 185 StructNlAttr(short type, @NonNull final InetAddress ip)186 public StructNlAttr(short type, @NonNull final InetAddress ip) { 187 nla_type = type; 188 setValue(ip.getAddress()); 189 } 190 StructNlAttr(short type, @NonNull final MacAddress mac)191 public StructNlAttr(short type, @NonNull final MacAddress mac) { 192 nla_type = type; 193 setValue(mac.toByteArray()); 194 } 195 StructNlAttr(short type, @NonNull final String string)196 public StructNlAttr(short type, @NonNull final String string) { 197 nla_type = type; 198 byte[] value = null; 199 try { 200 final byte[] stringBytes = string.getBytes("UTF-8"); 201 // Append '\0' at the end of interface name string bytes. 202 value = Arrays.copyOf(stringBytes, stringBytes.length + 1); 203 } catch (UnsupportedEncodingException ignored) { 204 // Do nothing. 205 } finally { 206 setValue(value); 207 } 208 } 209 StructNlAttr(short type, StructNlAttr... nested)210 public StructNlAttr(short type, StructNlAttr... nested) { 211 this(); 212 nla_type = makeNestedType(type); 213 214 int payloadLength = 0; 215 for (StructNlAttr nla : nested) payloadLength += nla.getAlignedLength(); 216 setValue(new byte[payloadLength]); 217 218 final ByteBuffer buf = getValueAsByteBuffer(); 219 for (StructNlAttr nla : nested) { 220 nla.pack(buf); 221 } 222 } 223 224 /** 225 * Get aligned attribute length. 226 */ getAlignedLength()227 public int getAlignedLength() { 228 return NetlinkConstants.alignedLengthOf(nla_len); 229 } 230 231 /** 232 * Get attribute value as BE16. 233 */ getValueAsBe16(short defaultValue)234 public short getValueAsBe16(short defaultValue) { 235 final ByteBuffer byteBuffer = getValueAsByteBuffer(); 236 if (byteBuffer == null || byteBuffer.remaining() != Short.BYTES) { 237 return defaultValue; 238 } 239 final ByteOrder originalOrder = byteBuffer.order(); 240 try { 241 byteBuffer.order(ByteOrder.BIG_ENDIAN); 242 return byteBuffer.getShort(); 243 } finally { 244 byteBuffer.order(originalOrder); 245 } 246 } 247 248 /** 249 * Get attribute value as BE32. 250 */ getValueAsBe32(int defaultValue)251 public int getValueAsBe32(int defaultValue) { 252 final ByteBuffer byteBuffer = getValueAsByteBuffer(); 253 if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) { 254 return defaultValue; 255 } 256 final ByteOrder originalOrder = byteBuffer.order(); 257 try { 258 byteBuffer.order(ByteOrder.BIG_ENDIAN); 259 return byteBuffer.getInt(); 260 } finally { 261 byteBuffer.order(originalOrder); 262 } 263 } 264 265 /** 266 * Get attribute value as ByteBuffer. 267 */ getValueAsByteBuffer()268 public ByteBuffer getValueAsByteBuffer() { 269 if (nla_value == null) return null; 270 final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value); 271 // By convention, all buffers in this library are in native byte order because netlink is in 272 // native byte order. It's the order that is used by NetlinkSocket.recvMessage and the only 273 // order accepted by NetlinkMessage.parse. 274 byteBuffer.order(ByteOrder.nativeOrder()); 275 return byteBuffer; 276 } 277 278 /** 279 * Get attribute value as byte. 280 */ getValueAsByte(byte defaultValue)281 public byte getValueAsByte(byte defaultValue) { 282 final ByteBuffer byteBuffer = getValueAsByteBuffer(); 283 if (byteBuffer == null || byteBuffer.remaining() != Byte.BYTES) { 284 return defaultValue; 285 } 286 return getValueAsByteBuffer().get(); 287 } 288 289 /** 290 * Get attribute value as Integer, or null if malformed (e.g., length is not 4 bytes). 291 */ getValueAsInteger()292 public Integer getValueAsInteger() { 293 final ByteBuffer byteBuffer = getValueAsByteBuffer(); 294 if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) { 295 return null; 296 } 297 return byteBuffer.getInt(); 298 } 299 300 /** 301 * Get attribute value as Int, default value if malformed. 302 */ getValueAsInt(int defaultValue)303 public int getValueAsInt(int defaultValue) { 304 final Integer value = getValueAsInteger(); 305 return (value != null) ? value : defaultValue; 306 } 307 308 /** 309 * Get attribute value as InetAddress. 310 * 311 * @return the InetAddress instance representation of attribute value or null if IP address 312 * is of illegal length. 313 */ 314 @Nullable getValueAsInetAddress()315 public InetAddress getValueAsInetAddress() { 316 if (nla_value == null) return null; 317 318 try { 319 return InetAddress.getByAddress(nla_value); 320 } catch (UnknownHostException ignored) { 321 return null; 322 } 323 } 324 325 /** 326 * Get attribute value as MacAddress. 327 * 328 * @return the MacAddress instance representation of attribute value or null if the given byte 329 * array is not a valid representation(e.g, not all link layers have 6-byte link-layer 330 * addresses) 331 */ 332 @Nullable getValueAsMacAddress()333 public MacAddress getValueAsMacAddress() { 334 if (nla_value == null) return null; 335 336 try { 337 return MacAddress.fromBytes(nla_value); 338 } catch (IllegalArgumentException ignored) { 339 return null; 340 } 341 } 342 343 /** 344 * Get attribute value as a unicode string. 345 * 346 * @return a unicode string or null if UTF-8 charset is not supported. 347 */ 348 @Nullable getValueAsString()349 public String getValueAsString() { 350 if (nla_value == null) return null; 351 // Check the attribute value length after removing string termination flag '\0'. 352 // This assumes that all netlink strings are null-terminated. 353 if (nla_value.length < (nla_len - NLA_HEADERLEN - 1)) return null; 354 355 try { 356 final byte[] array = Arrays.copyOf(nla_value, nla_len - NLA_HEADERLEN - 1); 357 return new String(array, "UTF-8"); 358 } catch (UnsupportedEncodingException | NegativeArraySizeException ignored) { 359 return null; 360 } 361 } 362 363 /** 364 * Write the netlink attribute to {@link ByteBuffer}. 365 */ pack(ByteBuffer byteBuffer)366 public void pack(ByteBuffer byteBuffer) { 367 final ByteOrder originalOrder = byteBuffer.order(); 368 final int originalPosition = byteBuffer.position(); 369 370 byteBuffer.order(ByteOrder.nativeOrder()); 371 try { 372 byteBuffer.putShort(nla_len); 373 byteBuffer.putShort(nla_type); 374 if (nla_value != null) byteBuffer.put(nla_value); 375 } finally { 376 byteBuffer.order(originalOrder); 377 } 378 byteBuffer.position(originalPosition + getAlignedLength()); 379 } 380 setValue(byte[] value)381 private void setValue(byte[] value) { 382 nla_value = value; 383 nla_len = (short) (NLA_HEADERLEN + ((nla_value != null) ? nla_value.length : 0)); 384 } 385 386 @Override toString()387 public String toString() { 388 return "StructNlAttr{ " 389 + "nla_len{" + nla_len + "}, " 390 + "nla_type{" + nla_type + "}, " 391 + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, " 392 + "}"; 393 } 394 } 395