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