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