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