1 /* 2 * Copyright (C) 2021 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.server.connectivity; 18 19 import android.util.Log; 20 import android.util.Range; 21 22 import com.android.net.module.util.Struct; 23 import com.android.net.module.util.Struct.Field; 24 import com.android.net.module.util.Struct.Type; 25 26 import java.net.Inet4Address; 27 import java.net.InetAddress; 28 import java.net.UnknownHostException; 29 30 /** Value type for DSCP setting and rewriting to DSCP policy BPF maps. */ 31 public class DscpPolicyValue extends Struct { 32 private static final String TAG = DscpPolicyValue.class.getSimpleName(); 33 34 @Field(order = 0, type = Type.ByteArray, arraysize = 16) 35 public final byte[] src46; 36 37 @Field(order = 1, type = Type.ByteArray, arraysize = 16) 38 public final byte[] dst46; 39 40 @Field(order = 2, type = Type.S32) 41 public final int ifIndex; 42 43 @Field(order = 3, type = Type.UBE16) 44 public final int srcPort; 45 46 @Field(order = 4, type = Type.U16) 47 public final int dstPortStart; 48 49 @Field(order = 5, type = Type.U16) 50 public final int dstPortEnd; 51 52 @Field(order = 6, type = Type.U8) 53 public final short proto; 54 55 @Field(order = 7, type = Type.S8) 56 public final byte dscp; 57 58 @Field(order = 8, type = Type.U8, padding = 3) 59 public final short mask; 60 61 private static final int SRC_IP_MASK = 0x1; 62 private static final int DST_IP_MASK = 0x02; 63 private static final int SRC_PORT_MASK = 0x4; 64 private static final int PROTO_MASK = 0x8; 65 ipEmpty(final byte[] ip)66 private boolean ipEmpty(final byte[] ip) { 67 for (int i = 0; i < ip.length; i++) { 68 if (ip[i] != 0) return false; 69 } 70 return true; 71 } 72 73 // TODO: move to frameworks/libs/net and have this and BpfCoordinator import it. toIpv4MappedAddressBytes(InetAddress ia)74 private byte[] toIpv4MappedAddressBytes(InetAddress ia) { 75 final byte[] addr6 = new byte[16]; 76 if (ia != null) { 77 final byte[] addr4 = ia.getAddress(); 78 addr6[10] = (byte) 0xff; 79 addr6[11] = (byte) 0xff; 80 addr6[12] = addr4[0]; 81 addr6[13] = addr4[1]; 82 addr6[14] = addr4[2]; 83 addr6[15] = addr4[3]; 84 } 85 return addr6; 86 } 87 toAddressField(InetAddress addr)88 private byte[] toAddressField(InetAddress addr) { 89 if (addr == null) { 90 return EMPTY_ADDRESS_FIELD; 91 } else if (addr instanceof Inet4Address) { 92 return toIpv4MappedAddressBytes(addr); 93 } else { 94 return addr.getAddress(); 95 } 96 } 97 98 private static final byte[] EMPTY_ADDRESS_FIELD = 99 InetAddress.parseNumericAddress("::").getAddress(); 100 makeMask(final byte[] src46, final byte[] dst46, final int srcPort, final int dstPortStart, final short proto, final byte dscp)101 private short makeMask(final byte[] src46, final byte[] dst46, final int srcPort, 102 final int dstPortStart, final short proto, final byte dscp) { 103 short mask = 0; 104 if (src46 != EMPTY_ADDRESS_FIELD) { 105 mask |= SRC_IP_MASK; 106 } 107 if (dst46 != EMPTY_ADDRESS_FIELD) { 108 mask |= DST_IP_MASK; 109 } 110 if (srcPort != -1) { 111 mask |= SRC_PORT_MASK; 112 } 113 if (proto != -1) { 114 mask |= PROTO_MASK; 115 } 116 return mask; 117 } 118 DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex, final int srcPort, final int dstPortStart, final int dstPortEnd, final short proto, final byte dscp)119 private DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex, 120 final int srcPort, final int dstPortStart, final int dstPortEnd, final short proto, 121 final byte dscp) { 122 this.src46 = toAddressField(src46); 123 this.dst46 = toAddressField(dst46); 124 this.ifIndex = ifIndex; 125 126 // These params need to be stored as 0 because uints are used in BpfMap. 127 // If they are -1 BpfMap write will throw errors. 128 this.srcPort = srcPort != -1 ? srcPort : 0; 129 this.dstPortStart = dstPortStart != -1 ? dstPortStart : 0; 130 this.dstPortEnd = dstPortEnd != -1 ? dstPortEnd : 65535; 131 this.proto = proto != -1 ? proto : 0; 132 133 this.dscp = dscp; 134 // Use member variables for IP since byte[] is needed and api variables for everything else 135 // so -1 is passed into mask if parameter is not present. 136 this.mask = makeMask(this.src46, this.dst46, srcPort, dstPortStart, proto, dscp); 137 } 138 DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex, final int srcPort, final Range<Integer> dstPort, final short proto, final byte dscp)139 public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int ifIndex, 140 final int srcPort, final Range<Integer> dstPort, final short proto, 141 final byte dscp) { 142 this(src46, dst46, ifIndex, srcPort, dstPort != null ? dstPort.getLower() : -1, 143 dstPort != null ? dstPort.getUpper() : -1, proto, dscp); 144 } 145 146 public static final DscpPolicyValue NONE = new DscpPolicyValue( 147 null /* src46 */, null /* dst46 */, 0 /* ifIndex */, -1 /* srcPort */, 148 -1 /* dstPortStart */, -1 /* dstPortEnd */, (short) -1 /* proto */, 149 (byte) -1 /* dscp */); 150 151 @Override toString()152 public String toString() { 153 String srcIpString = "empty"; 154 String dstIpString = "empty"; 155 156 // Separate try/catch for IP's so it's easier to debug. 157 try { 158 srcIpString = InetAddress.getByAddress(src46).getHostAddress(); 159 } catch (UnknownHostException e) { 160 Log.e(TAG, "Invalid SRC IP address", e); 161 } 162 163 try { 164 dstIpString = InetAddress.getByAddress(src46).getHostAddress(); 165 } catch (UnknownHostException e) { 166 Log.e(TAG, "Invalid DST IP address", e); 167 } 168 169 try { 170 return String.format( 171 "src46: %s, dst46: %s, ifIndex: %d, srcPort: %d, dstPortStart: %d," 172 + " dstPortEnd: %d, protocol: %d, dscp %s", srcIpString, dstIpString, 173 ifIndex, srcPort, dstPortStart, dstPortEnd, proto, dscp); 174 } catch (IllegalArgumentException e) { 175 return String.format("String format error: " + e); 176 } 177 } 178 } 179