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 android.net.netlink;
18 
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.nio.ByteOrder;
22 import java.nio.ByteBuffer;
23 
24 
25 /**
26  * struct nlattr
27  *
28  * see: <linux_src>/include/uapi/linux/netlink.h
29  *
30  * @hide
31  */
32 public class StructNlAttr {
33     // Already aligned.
34     public static final int NLA_HEADERLEN  = 4;
35     public static final int NLA_F_NESTED   = (1 << 15);
36 
makeNestedType(short type)37     public static short makeNestedType(short type) {
38         return (short) (type | NLA_F_NESTED);
39     }
40 
41     // Return a (length, type) object only, without consuming any bytes in
42     // |byteBuffer| and without copying or interpreting any value bytes.
43     // This is used for scanning over a packed set of struct nlattr's,
44     // looking for instances of a particular type.
peek(ByteBuffer byteBuffer)45     public static StructNlAttr peek(ByteBuffer byteBuffer) {
46         if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) {
47             return null;
48         }
49         final int baseOffset = byteBuffer.position();
50 
51         // Assume the byte order of the buffer is the expected byte order of the value.
52         final StructNlAttr struct = new StructNlAttr(byteBuffer.order());
53         // The byte order of nla_len and nla_type is always native.
54         final ByteOrder originalOrder = byteBuffer.order();
55         byteBuffer.order(ByteOrder.nativeOrder());
56         try {
57             struct.nla_len = byteBuffer.getShort();
58             struct.nla_type = byteBuffer.getShort();
59         } finally {
60             byteBuffer.order(originalOrder);
61         }
62 
63         byteBuffer.position(baseOffset);
64         if (struct.nla_len < NLA_HEADERLEN) {
65             // Malformed.
66             return null;
67         }
68         return struct;
69     }
70 
parse(ByteBuffer byteBuffer)71     public static StructNlAttr parse(ByteBuffer byteBuffer) {
72         final StructNlAttr struct = peek(byteBuffer);
73         if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) {
74             return null;
75         }
76 
77         final int baseOffset = byteBuffer.position();
78         byteBuffer.position(baseOffset + NLA_HEADERLEN);
79 
80         int valueLen = ((int) struct.nla_len) & 0xffff;
81         valueLen -= NLA_HEADERLEN;
82         if (valueLen > 0) {
83             struct.nla_value = new byte[valueLen];
84             byteBuffer.get(struct.nla_value, 0, valueLen);
85             byteBuffer.position(baseOffset + struct.getAlignedLength());
86         }
87         return struct;
88     }
89 
90     public short nla_len = (short) NLA_HEADERLEN;
91     public short nla_type;
92     public byte[] nla_value;
93 
94     // The byte order used to read/write the value member. Netlink length and
95     // type members are always read/written in native order.
96     private ByteOrder mByteOrder = ByteOrder.nativeOrder();
97 
StructNlAttr()98     public StructNlAttr() {}
99 
StructNlAttr(ByteOrder byteOrder)100     public StructNlAttr(ByteOrder byteOrder) {
101         mByteOrder = byteOrder;
102     }
103 
StructNlAttr(short type, byte value)104     public StructNlAttr(short type, byte value) {
105         nla_type = type;
106         setValue(new byte[1]);
107         nla_value[0] = value;
108     }
109 
StructNlAttr(short type, short value)110     public StructNlAttr(short type, short value) {
111         this(type, value, ByteOrder.nativeOrder());
112     }
113 
StructNlAttr(short type, short value, ByteOrder order)114     public StructNlAttr(short type, short value, ByteOrder order) {
115         this(order);
116         nla_type = type;
117         setValue(new byte[Short.BYTES]);
118         getValueAsByteBuffer().putShort(value);
119     }
120 
StructNlAttr(short type, int value)121     public StructNlAttr(short type, int value) {
122         this(type, value, ByteOrder.nativeOrder());
123     }
124 
StructNlAttr(short type, int value, ByteOrder order)125     public StructNlAttr(short type, int value, ByteOrder order) {
126         this(order);
127         nla_type = type;
128         setValue(new byte[Integer.BYTES]);
129         getValueAsByteBuffer().putInt(value);
130     }
131 
StructNlAttr(short type, InetAddress ip)132     public StructNlAttr(short type, InetAddress ip) {
133         nla_type = type;
134         setValue(ip.getAddress());
135     }
136 
StructNlAttr(short type, StructNlAttr... nested)137     public StructNlAttr(short type, StructNlAttr... nested) {
138         this();
139         nla_type = makeNestedType(type);
140 
141         int payloadLength = 0;
142         for (StructNlAttr nla : nested) payloadLength += nla.getAlignedLength();
143         setValue(new byte[payloadLength]);
144 
145         final ByteBuffer buf = getValueAsByteBuffer();
146         for (StructNlAttr nla : nested) {
147             nla.pack(buf);
148         }
149     }
150 
getAlignedLength()151     public int getAlignedLength() {
152         return NetlinkConstants.alignedLengthOf(nla_len);
153     }
154 
getValueAsByteBuffer()155     public ByteBuffer getValueAsByteBuffer() {
156         if (nla_value == null) { return null; }
157         final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value);
158         byteBuffer.order(mByteOrder);
159         return byteBuffer;
160     }
161 
getValueAsInt(int defaultValue)162     public int getValueAsInt(int defaultValue) {
163         final ByteBuffer byteBuffer = getValueAsByteBuffer();
164         if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
165             return defaultValue;
166         }
167         return getValueAsByteBuffer().getInt();
168     }
169 
getValueAsInetAddress()170     public InetAddress getValueAsInetAddress() {
171         if (nla_value == null) { return null; }
172 
173         try {
174             return InetAddress.getByAddress(nla_value);
175         } catch (UnknownHostException ignored) {
176             return null;
177         }
178     }
179 
pack(ByteBuffer byteBuffer)180     public void pack(ByteBuffer byteBuffer) {
181         final ByteOrder originalOrder = byteBuffer.order();
182         final int originalPosition = byteBuffer.position();
183 
184         byteBuffer.order(ByteOrder.nativeOrder());
185         try {
186             byteBuffer.putShort(nla_len);
187             byteBuffer.putShort(nla_type);
188             if (nla_value != null) byteBuffer.put(nla_value);
189         } finally {
190             byteBuffer.order(originalOrder);
191         }
192         byteBuffer.position(originalPosition + getAlignedLength());
193     }
194 
setValue(byte[] value)195     private void setValue(byte[] value) {
196         nla_value = value;
197         nla_len = (short) (NLA_HEADERLEN + ((nla_value != null) ? nla_value.length : 0));
198     }
199 
200     @Override
toString()201     public String toString() {
202         return "StructNlAttr{ "
203                 + "nla_len{" + nla_len + "}, "
204                 + "nla_type{" + nla_type + "}, "
205                 + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, "
206                 + "}";
207     }
208 }
209