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 static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
20 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
21 import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
22 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
23 
24 import android.net.netlink.StructNdaCacheInfo;
25 import android.net.netlink.StructNdMsg;
26 import android.net.netlink.StructNlAttr;
27 import android.net.netlink.StructNlMsgHdr;
28 import android.net.netlink.NetlinkMessage;
29 import android.system.OsConstants;
30 import android.util.Log;
31 
32 import java.net.InetAddress;
33 import java.net.Inet6Address;
34 import java.nio.ByteBuffer;
35 import java.nio.ByteOrder;
36 
37 
38 /**
39  * A NetlinkMessage subclass for netlink error messages.
40  *
41  * see also: <linux_src>/include/uapi/linux/neighbour.h
42  *
43  * @hide
44  */
45 public class RtNetlinkNeighborMessage extends NetlinkMessage {
46     public static final short NDA_UNSPEC    = 0;
47     public static final short NDA_DST       = 1;
48     public static final short NDA_LLADDR    = 2;
49     public static final short NDA_CACHEINFO = 3;
50     public static final short NDA_PROBES    = 4;
51     public static final short NDA_VLAN      = 5;
52     public static final short NDA_PORT      = 6;
53     public static final short NDA_VNI       = 7;
54     public static final short NDA_IFINDEX   = 8;
55     public static final short NDA_MASTER    = 9;
56 
findNextAttrOfType(short attrType, ByteBuffer byteBuffer)57     private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) {
58         while (byteBuffer != null && byteBuffer.remaining() > 0) {
59             final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
60             if (nlAttr == null) {
61                 break;
62             }
63             if (nlAttr.nla_type == attrType) {
64                 return StructNlAttr.parse(byteBuffer);
65             }
66             if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
67                 break;
68             }
69             byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
70         }
71         return null;
72     }
73 
parse(StructNlMsgHdr header, ByteBuffer byteBuffer)74     public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
75         final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header);
76 
77         neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer);
78         if (neighMsg.mNdmsg == null) {
79             return null;
80         }
81 
82         // Some of these are message-type dependent, and not always present.
83         final int baseOffset = byteBuffer.position();
84         StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer);
85         if (nlAttr != null) {
86             neighMsg.mDestination = nlAttr.getValueAsInetAddress();
87         }
88 
89         byteBuffer.position(baseOffset);
90         nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer);
91         if (nlAttr != null) {
92             neighMsg.mLinkLayerAddr = nlAttr.nla_value;
93         }
94 
95         byteBuffer.position(baseOffset);
96         nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer);
97         if (nlAttr != null) {
98             neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
99         }
100 
101         byteBuffer.position(baseOffset);
102         nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
103         if (nlAttr != null) {
104             neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
105         }
106 
107         final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
108         final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
109                 neighMsg.mHeader.nlmsg_len - kMinConsumed);
110         if (byteBuffer.remaining() < kAdditionalSpace) {
111             byteBuffer.position(byteBuffer.limit());
112         } else {
113             byteBuffer.position(baseOffset + kAdditionalSpace);
114         }
115 
116         return neighMsg;
117     }
118 
119     /**
120      * A convenience method to create an RTM_GETNEIGH request message.
121      */
newGetNeighborsRequest(int seqNo)122     public static byte[] newGetNeighborsRequest(int seqNo) {
123         final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
124         final byte[] bytes = new byte[length];
125         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
126         byteBuffer.order(ByteOrder.nativeOrder());
127 
128         final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
129         nlmsghdr.nlmsg_len = length;
130         nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
131         nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
132         nlmsghdr.nlmsg_seq = seqNo;
133         nlmsghdr.pack(byteBuffer);
134 
135         final StructNdMsg ndmsg = new StructNdMsg();
136         ndmsg.pack(byteBuffer);
137 
138         return bytes;
139     }
140 
141     /**
142      * A convenience method to create an RTM_NEWNEIGH message, to modify
143      * the kernel's state information for a specific neighbor.
144      */
newNewNeighborMessage( int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr)145     public static byte[] newNewNeighborMessage(
146             int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
147         final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
148         nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
149         nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
150         nlmsghdr.nlmsg_seq = seqNo;
151 
152         final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
153         msg.mNdmsg = new StructNdMsg();
154         msg.mNdmsg.ndm_family =
155                 (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
156         msg.mNdmsg.ndm_ifindex = ifIndex;
157         msg.mNdmsg.ndm_state = nudState;
158         msg.mDestination = ip;
159         msg.mLinkLayerAddr = llAddr;  // might be null
160 
161         final byte[] bytes = new byte[msg.getRequiredSpace()];
162         nlmsghdr.nlmsg_len = bytes.length;
163         final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
164         byteBuffer.order(ByteOrder.nativeOrder());
165         msg.pack(byteBuffer);
166         return bytes;
167     }
168 
169     private StructNdMsg mNdmsg;
170     private InetAddress mDestination;
171     private byte[] mLinkLayerAddr;
172     private int mNumProbes;
173     private StructNdaCacheInfo mCacheInfo;
174 
RtNetlinkNeighborMessage(StructNlMsgHdr header)175     private RtNetlinkNeighborMessage(StructNlMsgHdr header) {
176         super(header);
177         mNdmsg = null;
178         mDestination = null;
179         mLinkLayerAddr = null;
180         mNumProbes = 0;
181         mCacheInfo = null;
182     }
183 
getNdHeader()184     public StructNdMsg getNdHeader() {
185         return mNdmsg;
186     }
187 
getDestination()188     public InetAddress getDestination() {
189         return mDestination;
190     }
191 
getLinkLayerAddress()192     public byte[] getLinkLayerAddress() {
193         return mLinkLayerAddr;
194     }
195 
getProbes()196     public int getProbes() {
197         return mNumProbes;
198     }
199 
getCacheInfo()200     public StructNdaCacheInfo getCacheInfo() {
201         return mCacheInfo;
202     }
203 
getRequiredSpace()204     public int getRequiredSpace() {
205         int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
206         if (mDestination != null) {
207             spaceRequired += NetlinkConstants.alignedLengthOf(
208                     StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
209         }
210         if (mLinkLayerAddr != null) {
211             spaceRequired += NetlinkConstants.alignedLengthOf(
212                     StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
213         }
214         // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
215         // attributes appended.  Fix later, if necessary.
216         return spaceRequired;
217     }
218 
packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer)219     private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
220         final StructNlAttr nlAttr = new StructNlAttr();
221         nlAttr.nla_type = nlType;
222         nlAttr.nla_value = nlValue;
223         nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
224         nlAttr.pack(byteBuffer);
225     }
226 
pack(ByteBuffer byteBuffer)227     public void pack(ByteBuffer byteBuffer) {
228         getHeader().pack(byteBuffer) ;
229         mNdmsg.pack(byteBuffer);
230 
231         if (mDestination != null) {
232             packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
233         }
234         if (mLinkLayerAddr != null) {
235             packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
236         }
237     }
238 
239     @Override
toString()240     public String toString() {
241         final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
242         return "RtNetlinkNeighborMessage{ "
243                 + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
244                 + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, "
245                 + "destination{" + ipLiteral + "} "
246                 + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} "
247                 + "probes{" + mNumProbes + "} "
248                 + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} "
249                 + "}";
250     }
251 }
252