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