| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.netlink; |
| |
| import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK; |
| import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; |
| import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE; |
| import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; |
| |
| import android.net.netlink.StructNdaCacheInfo; |
| import android.net.netlink.StructNdMsg; |
| import android.net.netlink.StructNlAttr; |
| import android.net.netlink.StructNlMsgHdr; |
| import android.net.netlink.NetlinkMessage; |
| import android.system.OsConstants; |
| import android.util.Log; |
| |
| import java.net.InetAddress; |
| import java.net.Inet6Address; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| |
| |
| /** |
| * A NetlinkMessage subclass for rtnetlink neighbor messages. |
| * |
| * see also: <linux_src>/include/uapi/linux/neighbour.h |
| * |
| * @hide |
| */ |
| public class RtNetlinkNeighborMessage extends NetlinkMessage { |
| public static final short NDA_UNSPEC = 0; |
| public static final short NDA_DST = 1; |
| public static final short NDA_LLADDR = 2; |
| public static final short NDA_CACHEINFO = 3; |
| public static final short NDA_PROBES = 4; |
| public static final short NDA_VLAN = 5; |
| public static final short NDA_PORT = 6; |
| public static final short NDA_VNI = 7; |
| public static final short NDA_IFINDEX = 8; |
| public static final short NDA_MASTER = 9; |
| |
| private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) { |
| while (byteBuffer != null && byteBuffer.remaining() > 0) { |
| final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer); |
| if (nlAttr == null) { |
| break; |
| } |
| if (nlAttr.nla_type == attrType) { |
| return StructNlAttr.parse(byteBuffer); |
| } |
| if (byteBuffer.remaining() < nlAttr.getAlignedLength()) { |
| break; |
| } |
| byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength()); |
| } |
| return null; |
| } |
| |
| public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { |
| final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header); |
| |
| neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer); |
| if (neighMsg.mNdmsg == null) { |
| return null; |
| } |
| |
| // Some of these are message-type dependent, and not always present. |
| final int baseOffset = byteBuffer.position(); |
| StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer); |
| if (nlAttr != null) { |
| neighMsg.mDestination = nlAttr.getValueAsInetAddress(); |
| } |
| |
| byteBuffer.position(baseOffset); |
| nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer); |
| if (nlAttr != null) { |
| neighMsg.mLinkLayerAddr = nlAttr.nla_value; |
| } |
| |
| byteBuffer.position(baseOffset); |
| nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer); |
| if (nlAttr != null) { |
| neighMsg.mNumProbes = nlAttr.getValueAsInt(0); |
| } |
| |
| byteBuffer.position(baseOffset); |
| nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer); |
| if (nlAttr != null) { |
| neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer()); |
| } |
| |
| final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; |
| final int kAdditionalSpace = NetlinkConstants.alignedLengthOf( |
| neighMsg.mHeader.nlmsg_len - kMinConsumed); |
| if (byteBuffer.remaining() < kAdditionalSpace) { |
| byteBuffer.position(byteBuffer.limit()); |
| } else { |
| byteBuffer.position(baseOffset + kAdditionalSpace); |
| } |
| |
| return neighMsg; |
| } |
| |
| /** |
| * A convenience method to create an RTM_GETNEIGH request message. |
| */ |
| public static byte[] newGetNeighborsRequest(int seqNo) { |
| final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; |
| final byte[] bytes = new byte[length]; |
| final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); |
| byteBuffer.order(ByteOrder.nativeOrder()); |
| |
| final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); |
| nlmsghdr.nlmsg_len = length; |
| nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH; |
| nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
| nlmsghdr.nlmsg_seq = seqNo; |
| nlmsghdr.pack(byteBuffer); |
| |
| final StructNdMsg ndmsg = new StructNdMsg(); |
| ndmsg.pack(byteBuffer); |
| |
| return bytes; |
| } |
| |
| /** |
| * A convenience method to create an RTM_NEWNEIGH message, to modify |
| * the kernel's state information for a specific neighbor. |
| */ |
| public static byte[] newNewNeighborMessage( |
| int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) { |
| final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); |
| nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH; |
| nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; |
| nlmsghdr.nlmsg_seq = seqNo; |
| |
| final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr); |
| msg.mNdmsg = new StructNdMsg(); |
| msg.mNdmsg.ndm_family = |
| (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET); |
| msg.mNdmsg.ndm_ifindex = ifIndex; |
| msg.mNdmsg.ndm_state = nudState; |
| msg.mDestination = ip; |
| msg.mLinkLayerAddr = llAddr; // might be null |
| |
| final byte[] bytes = new byte[msg.getRequiredSpace()]; |
| nlmsghdr.nlmsg_len = bytes.length; |
| final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); |
| byteBuffer.order(ByteOrder.nativeOrder()); |
| msg.pack(byteBuffer); |
| return bytes; |
| } |
| |
| private StructNdMsg mNdmsg; |
| private InetAddress mDestination; |
| private byte[] mLinkLayerAddr; |
| private int mNumProbes; |
| private StructNdaCacheInfo mCacheInfo; |
| |
| private RtNetlinkNeighborMessage(StructNlMsgHdr header) { |
| super(header); |
| mNdmsg = null; |
| mDestination = null; |
| mLinkLayerAddr = null; |
| mNumProbes = 0; |
| mCacheInfo = null; |
| } |
| |
| public StructNdMsg getNdHeader() { |
| return mNdmsg; |
| } |
| |
| public InetAddress getDestination() { |
| return mDestination; |
| } |
| |
| public byte[] getLinkLayerAddress() { |
| return mLinkLayerAddr; |
| } |
| |
| public int getProbes() { |
| return mNumProbes; |
| } |
| |
| public StructNdaCacheInfo getCacheInfo() { |
| return mCacheInfo; |
| } |
| |
| public int getRequiredSpace() { |
| int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; |
| if (mDestination != null) { |
| spaceRequired += NetlinkConstants.alignedLengthOf( |
| StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length); |
| } |
| if (mLinkLayerAddr != null) { |
| spaceRequired += NetlinkConstants.alignedLengthOf( |
| StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length); |
| } |
| // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO |
| // attributes appended. Fix later, if necessary. |
| return spaceRequired; |
| } |
| |
| private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) { |
| final StructNlAttr nlAttr = new StructNlAttr(); |
| nlAttr.nla_type = nlType; |
| nlAttr.nla_value = nlValue; |
| nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length); |
| nlAttr.pack(byteBuffer); |
| } |
| |
| public void pack(ByteBuffer byteBuffer) { |
| getHeader().pack(byteBuffer) ; |
| mNdmsg.pack(byteBuffer); |
| |
| if (mDestination != null) { |
| packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer); |
| } |
| if (mLinkLayerAddr != null) { |
| packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress(); |
| return "RtNetlinkNeighborMessage{ " |
| + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, " |
| + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, " |
| + "destination{" + ipLiteral + "} " |
| + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} " |
| + "probes{" + mNumProbes + "} " |
| + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} " |
| + "}"; |
| } |
| } |