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