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
17package android.net.netlink;
18
19import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
20import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
21import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
22import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
23
24import android.net.netlink.StructNdaCacheInfo;
25import android.net.netlink.StructNdMsg;
26import android.net.netlink.StructNlAttr;
27import android.net.netlink.StructNlMsgHdr;
28import android.net.netlink.NetlinkMessage;
29import android.system.OsConstants;
30import android.util.Log;
31
32import java.net.InetAddress;
33import java.net.Inet6Address;
34import java.nio.ByteBuffer;
35import 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 */
45public 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
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
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     */
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     */
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
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
184    public StructNdMsg getNdHeader() {
185        return mNdmsg;
186    }
187
188    public InetAddress getDestination() {
189        return mDestination;
190    }
191
192    public byte[] getLinkLayerAddress() {
193        return mLinkLayerAddr;
194    }
195
196    public int getProbes() {
197        return mNumProbes;
198    }
199
200    public StructNdaCacheInfo getCacheInfo() {
201        return mCacheInfo;
202    }
203
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
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
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
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