Home | History | Annotate | Download | only in netlink
      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 rtnetlink neighbor 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 
     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