• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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