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 package com.android.networkstack.netlink; 17 18 import android.util.Log; 19 20 import androidx.annotation.NonNull; 21 import androidx.annotation.Nullable; 22 import androidx.annotation.VisibleForTesting; 23 24 import java.nio.BufferOverflowException; 25 import java.nio.BufferUnderflowException; 26 import java.nio.ByteBuffer; 27 import java.util.Objects; 28 29 /** 30 * Class for tcp_info. 31 * 32 * Corresponds to {@code struct tcp_info} from bionic/libc/kernel/uapi/linux/tcp.h 33 */ 34 public class TcpInfo { 35 public enum Field { 36 STATE(Byte.BYTES), 37 CASTATE(Byte.BYTES), 38 RETRANSMITS(Byte.BYTES), 39 PROBES(Byte.BYTES), 40 BACKOFF(Byte.BYTES), 41 OPTIONS(Byte.BYTES), 42 WSCALE(Byte.BYTES), 43 DELIVERY_RATE_APP_LIMITED(Byte.BYTES), 44 RTO(Integer.BYTES), 45 ATO(Integer.BYTES), 46 SND_MSS(Integer.BYTES), 47 RCV_MSS(Integer.BYTES), 48 UNACKED(Integer.BYTES), 49 SACKED(Integer.BYTES), 50 LOST(Integer.BYTES), 51 RETRANS(Integer.BYTES), 52 FACKETS(Integer.BYTES), 53 LAST_DATA_SENT(Integer.BYTES), 54 LAST_ACK_SENT(Integer.BYTES), 55 LAST_DATA_RECV(Integer.BYTES), 56 LAST_ACK_RECV(Integer.BYTES), 57 PMTU(Integer.BYTES), 58 RCV_SSTHRESH(Integer.BYTES), 59 RTT(Integer.BYTES), 60 RTTVAR(Integer.BYTES), 61 SND_SSTHRESH(Integer.BYTES), 62 SND_CWND(Integer.BYTES), 63 ADVMSS(Integer.BYTES), 64 REORDERING(Integer.BYTES), 65 RCV_RTT(Integer.BYTES), 66 RCV_SPACE(Integer.BYTES), 67 TOTAL_RETRANS(Integer.BYTES), 68 PACING_RATE(Long.BYTES), 69 MAX_PACING_RATE(Long.BYTES), 70 BYTES_ACKED(Long.BYTES), 71 BYTES_RECEIVED(Long.BYTES), 72 SEGS_OUT(Integer.BYTES), 73 SEGS_IN(Integer.BYTES), 74 NOTSENT_BYTES(Integer.BYTES), 75 MIN_RTT(Integer.BYTES), 76 DATA_SEGS_IN(Integer.BYTES), 77 DATA_SEGS_OUT(Integer.BYTES), 78 DELIVERY_RATE(Long.BYTES), 79 BUSY_TIME(Long.BYTES), 80 RWND_LIMITED(Long.BYTES), 81 SNDBUF_LIMITED(Long.BYTES); 82 83 public final int size; 84 Field(int s)85 Field(int s) { 86 size = s; 87 } 88 } 89 90 private static final String TAG = "TcpInfo"; 91 @VisibleForTesting 92 static final int LOST_OFFSET = getFieldOffset(Field.LOST); 93 @VisibleForTesting 94 static final int RETRANSMITS_OFFSET = getFieldOffset(Field.RETRANSMITS); 95 @VisibleForTesting 96 static final int SEGS_IN_OFFSET = getFieldOffset(Field.SEGS_IN); 97 @VisibleForTesting 98 static final int SEGS_OUT_OFFSET = getFieldOffset(Field.SEGS_OUT); 99 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 100 static final int TOTAL_RETRANS_OFFSET = getFieldOffset(Field.TOTAL_RETRANS); 101 /** 102 * This counts individual incoming packets that appeared on the wire, including: 103 * SYN, SYN-ACK, pure ACKs, data segments (after segmentation offload into small <=mtu 104 * packets), FIN, FIN-ACK, and any retransmits. 105 * 106 * This field is read from the tcpi_segs_in field from {@code struct tcp_info} 107 * in bionic/libc/kernel/uapi/linux/tcp.h. Also see [tcpEStatsPerfSegsIn] in the RFC4898. 108 */ 109 final int mSegsIn; 110 /** 111 * This counts individual outgoing packets that have been sent to the network, including: 112 * SYN, SYN-ACK, pure ACKs, data segments (after segmentation offload into small <=mtu 113 * packets), FIN, FIN-ACK, and any retransmits. 114 * 115 * This field is read from the tcpi_segs_out field from {@code struct tcp_info} 116 * in bionic/libc/kernel/uapi/linux/tcp.h. Also see [tcpEStatsPerfSegsOut] in the RFC4898. 117 */ 118 final int mSegsOut; 119 /** 120 * This counts individual accumulated retransmitted packets that have been sent to the network, 121 * including any retransmits for SYN, SYN-ACK, pure ACKs, data segments (after segmentation 122 * offload into small <=mtu packets), FIN and FIN-ACK. 123 * 124 * This field is read from the tcpi_total_retrans field from {@code struct tcp_info} 125 * in bionic/libc/kernel/uapi/linux/tcp.h. 126 */ 127 final int mTotalRetrans; 128 getFieldOffset(@onNull final Field needle)129 private static int getFieldOffset(@NonNull final Field needle) { 130 int offset = 0; 131 for (final Field field : Field.values()) { 132 if (field == needle) return offset; 133 offset += field.size; 134 } 135 throw new IllegalArgumentException("Unknown field"); 136 } 137 TcpInfo(@onNull ByteBuffer bytes, int infolen)138 private TcpInfo(@NonNull ByteBuffer bytes, int infolen) { 139 // SEGS_IN is the last required field in the buffer, so if the buffer is long enough for 140 // SEGS_IN it's long enough for everything 141 if (SEGS_IN_OFFSET + Field.SEGS_IN.size > infolen) { 142 throw new IllegalArgumentException("Length " + infolen + " is less than required."); 143 } 144 final int start = bytes.position(); 145 mSegsIn = bytes.getInt(start + SEGS_IN_OFFSET); 146 mSegsOut = bytes.getInt(start + SEGS_OUT_OFFSET); 147 mTotalRetrans = bytes.get(start + TOTAL_RETRANS_OFFSET); 148 // tcp_info structure grows over time as new fields are added. Jump to the end of the 149 // structure, as unknown fields might remain at the end of the structure if the tcp_info 150 // struct was expanded. 151 bytes.position(Math.min(infolen + start, bytes.limit())); 152 } 153 154 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) TcpInfo(int segsOut, int segsIn, int totalRetrans)155 TcpInfo(int segsOut, int segsIn, int totalRetrans) { 156 mSegsOut = segsOut; 157 mSegsIn = segsIn; 158 mTotalRetrans = totalRetrans; 159 } 160 161 /** Parse a TcpInfo from a giving ByteBuffer with a specific length. */ 162 @Nullable parse(@onNull ByteBuffer bytes, int infolen)163 public static TcpInfo parse(@NonNull ByteBuffer bytes, int infolen) { 164 try { 165 return new TcpInfo(bytes, infolen); 166 } catch (BufferUnderflowException | BufferOverflowException | IllegalArgumentException 167 | IndexOutOfBoundsException e) { 168 Log.e(TAG, "parsing error.", e); 169 return null; 170 } 171 } 172 173 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) decodeWscale(byte num)174 static String decodeWscale(byte num) { 175 return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f); 176 } 177 178 /** 179 * Returns a string representing a given tcp state. 180 * Map to enum in bionic/libc/include/netinet/tcp.h 181 */ 182 @VisibleForTesting getTcpStateName(int state)183 static String getTcpStateName(int state) { 184 switch (state) { 185 case 1: return "TCP_ESTABLISHED"; 186 case 2: return "TCP_SYN_SENT"; 187 case 3: return "TCP_SYN_RECV"; 188 case 4: return "TCP_FIN_WAIT1"; 189 case 5: return "TCP_FIN_WAIT2"; 190 case 6: return "TCP_TIME_WAIT"; 191 case 7: return "TCP_CLOSE"; 192 case 8: return "TCP_CLOSE_WAIT"; 193 case 9: return "TCP_LAST_ACK"; 194 case 10: return "TCP_LISTEN"; 195 case 11: return "TCP_CLOSING"; 196 default: return "UNKNOWN:" + Integer.toString(state); 197 } 198 } 199 200 @Override equals(Object obj)201 public boolean equals(Object obj) { 202 if (!(obj instanceof TcpInfo)) return false; 203 TcpInfo other = (TcpInfo) obj; 204 205 return mSegsIn == other.mSegsIn && mSegsOut == other.mSegsOut 206 && mTotalRetrans == other.mTotalRetrans; 207 } 208 209 @Override hashCode()210 public int hashCode() { 211 return Objects.hash(mSegsIn, mSegsOut, mTotalRetrans); 212 } 213 214 @Override toString()215 public String toString() { 216 return "TcpInfo{received=" + mSegsIn + ", sent=" + mSegsOut 217 + ", totalRetrans=" + mTotalRetrans + "}"; 218 } 219 } 220