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