From 6241874355c0e0d9ff04e993ad1d522c66b8c50b Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Wed, 13 Apr 2016 10:13:32 +0900 Subject: [PATCH] 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 --- .../android/net/metrics/DhcpErrorEvent.java | 93 +++++++++++++++++++ .../net/java/android/net/dhcp/DhcpPacket.java | 21 ++++- 2 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 core/java/android/net/metrics/DhcpErrorEvent.java diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java new file mode 100644 index 0000000000000..3869823750877 --- /dev/null +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -0,0 +1,93 @@ +/* + * 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 CREATOR + = new Parcelable.Creator() { + 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); + } +} diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index a8814088d489e..b184fe51c7d16 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -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); }