Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e649232e authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN Committed by Automerger Merge Worker
Browse files

Add utilities to filter packet am: c416cfb4 am: 1a5686e0 am: 146e2100...

Add utilities to filter packet am: c416cfb4 am: 1a5686e0 am: 146e2100 am: 7add1949 am: 6a37c0b4

Change-Id: Ic5141bb72b2ecf30fcc568bb4a4f3d39e44c6555
parents 6672de37 6a37c0b4
Loading
Loading
Loading
Loading
+87 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.testutils

import java.util.function.Predicate

const val ETHER_TYPE_OFFSET = 12
const val ETHER_HEADER_LENGTH = 14
const val IPV4_PROTOCOL_OFFSET = ETHER_HEADER_LENGTH + 9
const val IPV4_CHECKSUM_OFFSET = ETHER_HEADER_LENGTH + 10
const val IPV4_HEADER_LENGTH = 20
const val IPV4_UDP_OFFSET = ETHER_HEADER_LENGTH + IPV4_HEADER_LENGTH
const val BOOTP_OFFSET = IPV4_UDP_OFFSET + 8
const val BOOTP_TID_OFFSET = BOOTP_OFFSET + 4
const val BOOTP_CLIENT_MAC_OFFSET = BOOTP_OFFSET + 28
const val DHCP_OPTIONS_OFFSET = BOOTP_OFFSET + 240

/**
 * A [Predicate] that matches a [ByteArray] if it contains the specified [bytes] at the specified
 * [offset].
 */
class OffsetFilter(val offset: Int, vararg val bytes: Byte) : Predicate<ByteArray> {
    override fun test(packet: ByteArray) =
            bytes.withIndex().all { it.value == packet[offset + it.index] }
}

/**
 * A [Predicate] that matches ethernet-encapped packets that contain an UDP over IPv4 datagram.
 */
class IPv4UdpFilter : Predicate<ByteArray> {
    private val impl = OffsetFilter(ETHER_TYPE_OFFSET, 0x08, 0x00 /* IPv4 */).and(
            OffsetFilter(IPV4_PROTOCOL_OFFSET, 17 /* UDP */))
    override fun test(t: ByteArray) = impl.test(t)
}

/**
 * A [Predicate] that matches ethernet-encapped DHCP packets sent from a DHCP client.
 */
class DhcpClientPacketFilter : Predicate<ByteArray> {
    private val impl = IPv4UdpFilter()
            .and(OffsetFilter(IPV4_UDP_OFFSET /* source port */, 0x00, 0x44 /* 68 */))
            .and(OffsetFilter(IPV4_UDP_OFFSET + 2 /* dest port */, 0x00, 0x43 /* 67 */))
    override fun test(t: ByteArray) = impl.test(t)
}

/**
 * A [Predicate] that matches a [ByteArray] if it contains a ethernet-encapped DHCP packet that
 * contains the specified option with the specified [bytes] as value.
 */
class DhcpOptionFilter(val option: Byte, vararg val bytes: Byte) : Predicate<ByteArray> {
    override fun test(packet: ByteArray): Boolean {
        val option = findDhcpOption(packet, option) ?: return false
        return option.contentEquals(bytes)
    }
}

/**
 * Find a DHCP option in a packet and return its value, if found.
 */
fun findDhcpOption(packet: ByteArray, option: Byte): ByteArray? =
        findOptionOffset(packet, option, DHCP_OPTIONS_OFFSET)?.let {
            val optionLen = packet[it + 1]
            return packet.copyOfRange(it + 2 /* type, length bytes */, it + 2 + optionLen)
        }

private tailrec fun findOptionOffset(packet: ByteArray, option: Byte, searchOffset: Int): Int? {
    if (packet.size <= searchOffset + 2 /* type, length bytes */) return null

    return if (packet[searchOffset] == option) searchOffset else {
        val optionLen = packet[searchOffset + 1]
        findOptionOffset(packet, option, searchOffset + 2 + optionLen)
    }
}
+18 −11
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.testutils;
import android.net.util.PacketReader;
import android.net.util.PacketReader;
import android.os.Handler;
import android.os.Handler;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


import java.io.FileDescriptor;
import java.io.FileDescriptor;
@@ -26,12 +27,16 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Predicate;
import java.util.concurrent.TimeUnit;

import kotlin.Lazy;
import kotlin.LazyKt;


public class TapPacketReader extends PacketReader {
public class TapPacketReader extends PacketReader {
    private final FileDescriptor mTapFd;
    private final FileDescriptor mTapFd;
    private final LinkedBlockingQueue<byte[]> mReceivedPackets = new LinkedBlockingQueue<byte[]>();
    private final ArrayTrackRecord<byte[]> mReceivedPackets = new ArrayTrackRecord<>();
    private final Lazy<ArrayTrackRecord<byte[]>.ReadHead> mReadHead =
            LazyKt.lazy(mReceivedPackets::newReadHead);


    public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) {
    public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) {
        super(h, maxPacketSize);
        super(h, maxPacketSize);
@@ -46,23 +51,25 @@ public class TapPacketReader extends PacketReader {
    @Override
    @Override
    protected void handlePacket(byte[] recvbuf, int length) {
    protected void handlePacket(byte[] recvbuf, int length) {
        final byte[] newPacket = Arrays.copyOf(recvbuf, length);
        final byte[] newPacket = Arrays.copyOf(recvbuf, length);
        if (!mReceivedPackets.offer(newPacket)) {
        if (!mReceivedPackets.add(newPacket)) {
            throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!");
            throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!");
        }
        }
    }
    }


    /**
    /**
     * Get the next packet that was received on the interface.
     * Get the next packet that was received on the interface.
     *
     */
     */
    @Nullable
    @Nullable
    public byte[] popPacket(long timeoutMs) {
    public byte[] popPacket(long timeoutMs) {
        try {
        return mReadHead.getValue().poll(timeoutMs, packet -> true);
            return mReceivedPackets.poll(timeoutMs, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            // Fall through
    }
    }
        return null;

    /**
     * Get the next packet that was received on the interface and matches the specified filter.
     */
    @Nullable
    public byte[] popPacket(long timeoutMs, @NonNull Predicate<byte[]> filter) {
        return mReadHead.getValue().poll(timeoutMs, filter::test);
    }
    }


    public void sendResponse(final ByteBuffer packet) throws IOException {
    public void sendResponse(final ByteBuffer packet) throws IOException {