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

Commit 62418743 authored by Hugo Benichi's avatar Hugo Benichi
Browse files

Add DHCP error event class and record DHCP errors.

This patch adds an event class derived from IpConnectivityEvent that
records parsing errors of DHCP response packets.

Change-Id: I19516cf05e3419c4262e3236899e52987e5f2264
parent 57767296
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.metrics;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * {@hide} Event class used to record error events when parsing DHCP response packets.
 */
public class DhcpErrorEvent extends IpConnectivityEvent implements Parcelable {
    public static final String TAG = "DhcpErrorEvent";

    public static final int L2_ERROR   = 1;
    public static final int L3_ERROR   = 2;
    public static final int L4_ERROR   = 3;
    public static final int DHCP_ERROR = 4;
    public static final int MISC_ERROR = 5;

    public static final int L2_TOO_SHORT               = makeErrorCode(L2_ERROR, 1);
    public static final int L2_WRONG_ETH_TYPE          = makeErrorCode(L2_ERROR, 2);

    public static final int L3_TOO_SHORT               = makeErrorCode(L3_ERROR, 1);
    public static final int L3_NOT_IPV4                = makeErrorCode(L3_ERROR, 2);
    public static final int L3_INVALID_IP              = makeErrorCode(L3_ERROR, 3);

    public static final int L4_NOT_UDP                 = makeErrorCode(L4_ERROR, 1);
    public static final int L4_WRONG_PORT              = makeErrorCode(L4_ERROR, 2);

    public static final int BOOTP_TOO_SHORT            = makeErrorCode(DHCP_ERROR, 1);
    public static final int DHCP_BAD_MAGIC_COOKIE      = makeErrorCode(DHCP_ERROR, 2);
    public static final int DHCP_INVALID_OPTION_LENGTH = makeErrorCode(DHCP_ERROR, 3);
    public static final int DHCP_NO_MSG_TYPE           = makeErrorCode(DHCP_ERROR, 4);
    public static final int DHCP_UNKNOWN_MSG_TYPE      = makeErrorCode(DHCP_ERROR, 5);

    public static final int BUFFER_UNDERFLOW           = makeErrorCode(MISC_ERROR, 1);

    // error code byte format (MSB to LSB):
    // byte 0: error type
    // byte 1: error subtype
    // byte 2: unused
    // byte 3: optional code
    public final int errorCode;

    private DhcpErrorEvent(int errorCode) {
        this.errorCode = errorCode;
    }

    private DhcpErrorEvent(Parcel in) {
        this.errorCode = in.readInt();
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(errorCode);
    }

    public static final Parcelable.Creator<DhcpErrorEvent> CREATOR
        = new Parcelable.Creator<DhcpErrorEvent>() {
        public DhcpErrorEvent createFromParcel(Parcel in) {
            return new DhcpErrorEvent(in);
        }

        public DhcpErrorEvent[] newArray(int size) {
            return new DhcpErrorEvent[size];
        }
    };

    public static void logEvent(int errorCode) {
        IpConnectivityEvent.logEvent(IPCE_DHCP_PARSE_ERROR, new DhcpErrorEvent(errorCode));
    }

    public static void logEvent(int errorCode, int option) {
        logEvent((0xFFFF0000 & errorCode) | (0xFF & option));
    }

    private static int makeErrorCode(int type, int subtype) {
        return (type << 24) | ((0xFF & subtype) << 16);
    }
}
+18 −3
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package android.net.dhcp;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.net.metrics.DhcpErrorEvent;
import android.os.Build;
import android.os.SystemProperties;
import android.system.OsConstants;
@@ -765,6 +766,7 @@ abstract class DhcpPacket {
        // check to see if we need to parse L2, IP, and UDP encaps
        if (pktType == ENCAP_L2) {
            if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L2_TOO_SHORT);
                throw new ParseException("L2 packet too short, %d < %d",
                        packet.remaining(), MIN_PACKET_LENGTH_L2);
            }
@@ -777,13 +779,16 @@ abstract class DhcpPacket {

            short l2type = packet.getShort();

            if (l2type != OsConstants.ETH_P_IP)
            if (l2type != OsConstants.ETH_P_IP) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L2_WRONG_ETH_TYPE);
                throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x",
                        l2type, OsConstants.ETH_P_IP);
            }
        }

        if (pktType <= ENCAP_L3) {
            if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_TOO_SHORT);
                throw new ParseException("L3 packet too short, %d < %d",
                        packet.remaining(), MIN_PACKET_LENGTH_L3);
            }
@@ -791,6 +796,7 @@ abstract class DhcpPacket {
            byte ipTypeAndLength = packet.get();
            int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
            if (ipVersion != 4) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_NOT_IPV4);
                throw new ParseException("Invalid IP version %d", ipVersion);
            }

@@ -808,6 +814,7 @@ abstract class DhcpPacket {
            ipDst = readIpAddress(packet);

            if (ipProto != IP_TYPE_UDP) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L4_NOT_UDP);
                throw new ParseException("Protocol not UDP: %d", ipProto);
            }

@@ -834,12 +841,14 @@ abstract class DhcpPacket {
                // socket to drop packets that don't have the right source ports. However, it's
                // possible that a packet arrives between when the socket is bound and when the
                // filter is set. http://b/26696823 .
                DhcpErrorEvent.logEvent(DhcpErrorEvent.L4_WRONG_PORT);
                throw new ParseException("Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
            }
        }

        // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
        if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
            DhcpErrorEvent.logEvent(DhcpErrorEvent.BOOTP_TOO_SHORT);
            throw new ParseException("Invalid type or BOOTP packet too short, %d < %d",
                        packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
        }
@@ -864,6 +873,7 @@ abstract class DhcpPacket {
            packet.get(ipv4addr);
            relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
        } catch (UnknownHostException ex) {
            DhcpErrorEvent.logEvent(DhcpErrorEvent.L3_INVALID_IP);
            throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
        }

@@ -888,6 +898,7 @@ abstract class DhcpPacket {
        int dhcpMagicCookie = packet.getInt();

        if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
            DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE);
            throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie,
                    DHCP_MAGIC_COOKIE);
        }
@@ -896,9 +907,8 @@ abstract class DhcpPacket {
        boolean notFinishedOptions = true;

        while ((packet.position() < packet.limit()) && notFinishedOptions) {
            final byte optionType = packet.get(); // cannot underflow because position < limit
            try {
                byte optionType = packet.get();

                if (optionType == DHCP_OPTION_END) {
                    notFinishedOptions = false;
                } else if (optionType == DHCP_OPTION_PAD) {
@@ -999,11 +1009,14 @@ abstract class DhcpPacket {
                    }

                    if (expectedLen != optionLen) {
                        DhcpErrorEvent.logEvent(
                                DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
                        throw new ParseException("Invalid length %d for option %d, expected %d",
                                optionLen, optionType, expectedLen);
                    }
                }
            } catch (BufferUnderflowException e) {
                DhcpErrorEvent.logEvent(DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
                throw new ParseException("BufferUnderflowException");
            }
        }
@@ -1012,6 +1025,7 @@ abstract class DhcpPacket {

        switch(dhcpType) {
            case (byte) 0xFF:
                DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_NO_MSG_TYPE);
                throw new ParseException("No DHCP message type option");
            case DHCP_MESSAGE_TYPE_DISCOVER:
                newPacket = new DhcpDiscoverPacket(
@@ -1045,6 +1059,7 @@ abstract class DhcpPacket {
                    clientMac);
                break;
            default:
                DhcpErrorEvent.logEvent(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE);
                throw new ParseException("Unimplemented DHCP type %d", dhcpType);
        }