1 /* 2 * Copyright (C) 2023 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 com.android.testutils 18 19 import android.net.MacAddress 20 import android.util.Log 21 import com.android.net.module.util.Ipv6Utils 22 import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN 23 import com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA 24 import com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED 25 import com.android.net.module.util.Struct 26 import com.android.net.module.util.structs.Icmpv6Header 27 import com.android.net.module.util.structs.Ipv6Header 28 import com.android.net.module.util.structs.LlaOption 29 import com.android.net.module.util.structs.NsHeader 30 import com.android.testutils.PacketReflector.IPV6_HEADER_LENGTH 31 import java.lang.IllegalArgumentException 32 import java.net.Inet6Address 33 import java.nio.ByteBuffer 34 35 private const val NS_TYPE = 135.toShort() 36 37 /** 38 * A class that can be used to reply to Neighbor Solicitation packets on a [TapPacketReader]. 39 */ 40 class NSResponder( 41 reader: TapPacketReader, 42 table: Map<Inet6Address, MacAddress>, 43 name: String = NSResponder::class.java.simpleName 44 ) : PacketResponder(reader, Icmpv6Filter(), name) { 45 companion object { 46 private val TAG = NSResponder::class.simpleName 47 } 48 49 // Copy the map if not already immutable (toMap) to make sure it is not modified 50 private val table = table.toMap() 51 replyToPacketnull52 override fun replyToPacket(packet: ByteArray, reader: TapPacketReader) { 53 if (packet.size < IPV6_HEADER_LENGTH) { 54 return 55 } 56 val buf = ByteBuffer.wrap(packet, ETHER_HEADER_LEN, packet.size - ETHER_HEADER_LEN) 57 val ipv6Header = parseOrLog(Ipv6Header::class.java, buf) ?: return 58 val icmpHeader = parseOrLog(Icmpv6Header::class.java, buf) ?: return 59 if (icmpHeader.type != NS_TYPE) { 60 return 61 } 62 val ns = parseOrLog(NsHeader::class.java, buf) ?: return 63 val replyMacAddr = table[ns.target] ?: return 64 val slla = parseOrLog(LlaOption::class.java, buf) ?: return 65 val requesterMac = slla.linkLayerAddress 66 67 val tlla = LlaOption.build(ICMPV6_ND_OPTION_TLLA.toByte(), replyMacAddr) 68 reader.sendResponse(Ipv6Utils.buildNaPacket( 69 replyMacAddr /* srcMac */, 70 requesterMac /* dstMac */, 71 ns.target /* srcIp */, 72 ipv6Header.srcIp /* dstIp */, 73 NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED, 74 ns.target, 75 tlla)) 76 } 77 parseOrLognull78 private fun <T> parseOrLog(clazz: Class<T>, buf: ByteBuffer): T? where T : Struct { 79 return try { 80 Struct.parse(clazz, buf) 81 } catch (e: IllegalArgumentException) { 82 Log.e(TAG, "Invalid ${clazz.simpleName} in ICMPv6 packet", e) 83 null 84 } 85 } 86 } 87