• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 android.net.apf;
18 
19 import static android.system.OsConstants.*;
20 
21 import static com.android.internal.util.BitUtils.bytesToBEInt;
22 import static com.android.internal.util.BitUtils.getUint16;
23 import static com.android.internal.util.BitUtils.getUint32;
24 import static com.android.internal.util.BitUtils.getUint8;
25 import static com.android.internal.util.BitUtils.uint16;
26 import static com.android.internal.util.BitUtils.uint32;
27 import static com.android.internal.util.BitUtils.uint8;
28 
29 import android.os.SystemClock;
30 import android.net.LinkAddress;
31 import android.net.LinkProperties;
32 import android.net.NetworkUtils;
33 import android.net.apf.ApfGenerator;
34 import android.net.apf.ApfGenerator.IllegalInstructionException;
35 import android.net.apf.ApfGenerator.Register;
36 import android.net.ip.IpManager;
37 import android.net.metrics.ApfProgramEvent;
38 import android.net.metrics.ApfStats;
39 import android.net.metrics.IpConnectivityLog;
40 import android.net.metrics.RaEvent;
41 import android.system.ErrnoException;
42 import android.system.Os;
43 import android.system.PacketSocketAddress;
44 import android.text.format.DateUtils;
45 import android.util.Log;
46 import android.util.Pair;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.util.HexDump;
51 import com.android.internal.util.IndentingPrintWriter;
52 
53 import java.io.FileDescriptor;
54 import java.io.IOException;
55 import java.lang.Thread;
56 import java.net.Inet4Address;
57 import java.net.Inet6Address;
58 import java.net.InetAddress;
59 import java.net.NetworkInterface;
60 import java.net.SocketException;
61 import java.net.UnknownHostException;
62 import java.nio.ByteBuffer;
63 import java.nio.BufferUnderflowException;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 
67 import libcore.io.IoBridge;
68 
69 /**
70  * For networks that support packet filtering via APF programs, {@code ApfFilter}
71  * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
72  * filter out redundant duplicate ones.
73  *
74  * Threading model:
75  * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
76  * know what RAs to filter for, thus generating APF programs is dependent on mRas.
77  * mRas can be accessed by multiple threads:
78  * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
79  * - callers of:
80  *    - setMulticastFilter(), which can cause an APF program to be generated.
81  *    - dump(), which dumps mRas among other things.
82  *    - shutdown(), which clears mRas.
83  * So access to mRas is synchronized.
84  *
85  * @hide
86  */
87 public class ApfFilter {
88 
89     // Enums describing the outcome of receiving an RA packet.
90     private static enum ProcessRaResult {
91         MATCH,          // Received RA matched a known RA
92         DROPPED,        // Received RA ignored due to MAX_RAS
93         PARSE_ERROR,    // Received RA could not be parsed
94         ZERO_LIFETIME,  // Received RA had 0 lifetime
95         UPDATE_NEW_RA,  // APF program updated for new RA
96         UPDATE_EXPIRY   // APF program updated for expiry
97     }
98 
99     // Thread to listen for RAs.
100     @VisibleForTesting
101     class ReceiveThread extends Thread {
102         private final byte[] mPacket = new byte[1514];
103         private final FileDescriptor mSocket;
104         private final long mStart = SystemClock.elapsedRealtime();
105         private final ApfStats mStats = new ApfStats();
106 
107         private volatile boolean mStopped;
108 
ReceiveThread(FileDescriptor socket)109         public ReceiveThread(FileDescriptor socket) {
110             mSocket = socket;
111         }
112 
halt()113         public void halt() {
114             mStopped = true;
115             try {
116                 // Interrupts the read() call the thread is blocked in.
117                 IoBridge.closeAndSignalBlockedThreads(mSocket);
118             } catch (IOException ignored) {}
119         }
120 
121         @Override
run()122         public void run() {
123             log("begin monitoring");
124             while (!mStopped) {
125                 try {
126                     int length = Os.read(mSocket, mPacket, 0, mPacket.length);
127                     updateStats(processRa(mPacket, length));
128                 } catch (IOException|ErrnoException e) {
129                     if (!mStopped) {
130                         Log.e(TAG, "Read error", e);
131                     }
132                 }
133             }
134             logStats();
135         }
136 
updateStats(ProcessRaResult result)137         private void updateStats(ProcessRaResult result) {
138             mStats.receivedRas++;
139             switch(result) {
140                 case MATCH:
141                     mStats.matchingRas++;
142                     return;
143                 case DROPPED:
144                     mStats.droppedRas++;
145                     return;
146                 case PARSE_ERROR:
147                     mStats.parseErrors++;
148                     return;
149                 case ZERO_LIFETIME:
150                     mStats.zeroLifetimeRas++;
151                     return;
152                 case UPDATE_EXPIRY:
153                     mStats.matchingRas++;
154                     mStats.programUpdates++;
155                     return;
156                 case UPDATE_NEW_RA:
157                     mStats.programUpdates++;
158                     return;
159             }
160         }
161 
logStats()162         private void logStats() {
163             final long nowMs = SystemClock.elapsedRealtime();
164             synchronized (this) {
165                 mStats.durationMs = nowMs - mStart;
166                 mStats.maxProgramSize = mApfCapabilities.maximumApfProgramSize;
167                 mStats.programUpdatesAll = mNumProgramUpdates;
168                 mStats.programUpdatesAllowingMulticast = mNumProgramUpdatesAllowingMulticast;
169                 mMetricsLog.log(mStats);
170                 logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
171             }
172         }
173     }
174 
175     private static final String TAG = "ApfFilter";
176     private static final boolean DBG = true;
177     private static final boolean VDBG = false;
178 
179     private static final int ETH_HEADER_LEN = 14;
180     private static final int ETH_DEST_ADDR_OFFSET = 0;
181     private static final int ETH_ETHERTYPE_OFFSET = 12;
182     private static final int ETH_TYPE_MIN = 0x0600;
183     private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
184             {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
185     // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
186     private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
187     // Endianness is not an issue for this constant because the APF interpreter always operates in
188     // network byte order.
189     private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
190     private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
191     private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
192     private static final int IPV4_ANY_HOST_ADDRESS = 0;
193     private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
194 
195     // Traffic class and Flow label are not byte aligned. Luckily we
196     // don't care about either value so we'll consider bytes 1-3 of the
197     // IPv6 header as don't care.
198     private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1;
199     private static final int IPV6_FLOW_LABEL_LEN = 3;
200     private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
201     private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
202     private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
203     private static final int IPV6_HEADER_LEN = 40;
204     // The IPv6 all nodes address ff02::1
205     private static final byte[] IPV6_ALL_NODES_ADDRESS =
206             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
207 
208     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
209     private static final int ICMP6_ROUTER_SOLICITATION = 133;
210     private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
211     private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
212     private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
213 
214     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
215     private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
216     private static final int UDP_HEADER_LEN = 8;
217 
218     private static final int DHCP_CLIENT_PORT = 68;
219     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
220     private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
221 
222     private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
223     private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
224     private static final short ARP_OPCODE_REQUEST = 1;
225     private static final short ARP_OPCODE_REPLY = 2;
226     private static final byte[] ARP_IPV4_HEADER = {
227             0, 1, // Hardware type: Ethernet (1)
228             8, 0, // Protocol type: IP (0x0800)
229             6,    // Hardware size: 6
230             4,    // Protocol size: 4
231     };
232     private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
233     // Do not log ApfProgramEvents whose actual lifetimes was less than this.
234     private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
235 
236     private final ApfCapabilities mApfCapabilities;
237     private final IpManager.Callback mIpManagerCallback;
238     private final NetworkInterface mNetworkInterface;
239     private final IpConnectivityLog mMetricsLog;
240 
241     @VisibleForTesting
242     byte[] mHardwareAddress;
243     @VisibleForTesting
244     ReceiveThread mReceiveThread;
245     @GuardedBy("this")
246     private long mUniqueCounter;
247     @GuardedBy("this")
248     private boolean mMulticastFilter;
249     private final boolean mDrop802_3Frames;
250     // Our IPv4 address, if we have just one, otherwise null.
251     @GuardedBy("this")
252     private byte[] mIPv4Address;
253     // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
254     @GuardedBy("this")
255     private int mIPv4PrefixLength;
256 
257     @VisibleForTesting
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface, IpManager.Callback ipManagerCallback, boolean multicastFilter, boolean ieee802_3Filter, IpConnectivityLog log)258     ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
259             IpManager.Callback ipManagerCallback, boolean multicastFilter,
260             boolean ieee802_3Filter, IpConnectivityLog log) {
261         mApfCapabilities = apfCapabilities;
262         mIpManagerCallback = ipManagerCallback;
263         mNetworkInterface = networkInterface;
264         mMulticastFilter = multicastFilter;
265         mDrop802_3Frames = ieee802_3Filter;
266         mMetricsLog = log;
267 
268         // TODO: ApfFilter should not generate programs until IpManager sends provisioning success.
269         maybeStartFilter();
270     }
271 
log(String s)272     private void log(String s) {
273         Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
274     }
275 
276     @GuardedBy("this")
getUniqueNumberLocked()277     private long getUniqueNumberLocked() {
278         return mUniqueCounter++;
279     }
280 
281     /**
282      * Attempt to start listening for RAs and, if RAs are received, generating and installing
283      * filters to ignore useless RAs.
284      */
285     @VisibleForTesting
maybeStartFilter()286     void maybeStartFilter() {
287         FileDescriptor socket;
288         try {
289             mHardwareAddress = mNetworkInterface.getHardwareAddress();
290             synchronized(this) {
291                 // Install basic filters
292                 installNewProgramLocked();
293             }
294             socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
295             PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
296                     mNetworkInterface.getIndex());
297             Os.bind(socket, addr);
298             NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
299         } catch(SocketException|ErrnoException e) {
300             Log.e(TAG, "Error starting filter", e);
301             return;
302         }
303         mReceiveThread = new ReceiveThread(socket);
304         mReceiveThread.start();
305     }
306 
307     // Returns seconds since device boot.
308     @VisibleForTesting
currentTimeSeconds()309     protected long currentTimeSeconds() {
310         return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
311     }
312 
313     public static class InvalidRaException extends Exception {
InvalidRaException(String m)314         public InvalidRaException(String m) {
315             super(m);
316         }
317     }
318 
319     // A class to hold information about an RA.
320     @VisibleForTesting
321     class Ra {
322         // From RFC4861:
323         private static final int ICMP6_RA_HEADER_LEN = 16;
324         private static final int ICMP6_RA_CHECKSUM_OFFSET =
325                 ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
326         private static final int ICMP6_RA_CHECKSUM_LEN = 2;
327         private static final int ICMP6_RA_OPTION_OFFSET =
328                 ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
329         private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
330                 ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
331         private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
332         // Prefix information option.
333         private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
334         private static final int ICMP6_PREFIX_OPTION_LEN = 32;
335         private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
336         private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
337         private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
338         private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
339 
340         // From RFC6106: Recursive DNS Server option
341         private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
342         // From RFC6106: DNS Search List option
343         private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
344 
345         // From RFC4191: Route Information option
346         private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
347         // Above three options all have the same format:
348         private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
349         private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
350 
351         // Note: mPacket's position() cannot be assumed to be reset.
352         private final ByteBuffer mPacket;
353         // List of binary ranges that include the whole packet except the lifetimes.
354         // Pairs consist of offset and length.
355         private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
356                 new ArrayList<Pair<Integer, Integer>>();
357         // Minimum lifetime in packet
358         long mMinLifetime;
359         // When the packet was last captured, in seconds since Unix Epoch
360         long mLastSeen;
361 
362         // For debugging only. Offsets into the packet where PIOs are.
363         private final ArrayList<Integer> mPrefixOptionOffsets = new ArrayList<>();
364 
365         // For debugging only. Offsets into the packet where RDNSS options are.
366         private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>();
367 
368         // For debugging only. How many times this RA was seen.
369         int seenCount = 0;
370 
371         // For debugging only. Returns the hex representation of the last matching packet.
getLastMatchingPacket()372         String getLastMatchingPacket() {
373             return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(),
374                     false /* lowercase */);
375         }
376 
377         // For debugging only. Returns the string representation of the IPv6 address starting at
378         // position pos in the packet.
IPv6AddresstoString(int pos)379         private String IPv6AddresstoString(int pos) {
380             try {
381                 byte[] array = mPacket.array();
382                 // Can't just call copyOfRange() and see if it throws, because if it reads past the
383                 // end it pads with zeros instead of throwing.
384                 if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
385                     return "???";
386                 }
387                 byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
388                 InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
389                 return address.getHostAddress();
390             } catch (UnsupportedOperationException e) {
391                 // array() failed. Cannot happen, mPacket is array-backed and read-write.
392                 return "???";
393             } catch (ClassCastException|UnknownHostException e) {
394                 // Cannot happen.
395                 return "???";
396             }
397         }
398 
399         // Can't be static because it's in a non-static inner class.
400         // TODO: Make this static once RA is its own class.
prefixOptionToString(StringBuffer sb, int offset)401         private void prefixOptionToString(StringBuffer sb, int offset) {
402             String prefix = IPv6AddresstoString(offset + 16);
403             int length = getUint8(mPacket, offset + 2);
404             long valid = getUint32(mPacket, offset + 4);
405             long preferred = getUint32(mPacket, offset + 8);
406             sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred));
407         }
408 
rdnssOptionToString(StringBuffer sb, int offset)409         private void rdnssOptionToString(StringBuffer sb, int offset) {
410             int optLen = getUint8(mPacket, offset + 1) * 8;
411             if (optLen < 24) return;  // Malformed or empty.
412             long lifetime = getUint32(mPacket, offset + 4);
413             int numServers = (optLen - 8) / 16;
414             sb.append("DNS ").append(lifetime).append("s");
415             for (int server = 0; server < numServers; server++) {
416                 sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server));
417             }
418         }
419 
toString()420         public String toString() {
421             try {
422                 StringBuffer sb = new StringBuffer();
423                 sb.append(String.format("RA %s -> %s %ds ",
424                         IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
425                         IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
426                         getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)));
427                 for (int i: mPrefixOptionOffsets) {
428                     prefixOptionToString(sb, i);
429                 }
430                 for (int i: mRdnssOptionOffsets) {
431                     rdnssOptionToString(sb, i);
432                 }
433                 return sb.toString();
434             } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
435                 return "<Malformed RA>";
436             }
437         }
438 
439         /**
440          * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
441          * Assumes mPacket.position() is as far as we've parsed the packet.
442          * @param lastNonLifetimeStart offset within packet of where the last binary range of
443          *                             data not including a lifetime.
444          * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
445          * @param lifetimeLength length of the next lifetime data.
446          * @return offset within packet of where the next binary range of data not including
447          *         a lifetime. This can be passed into the next invocation of this function
448          *         via {@code lastNonLifetimeStart}.
449          */
addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset, int lifetimeLength)450         private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
451                 int lifetimeLength) {
452             lifetimeOffset += mPacket.position();
453             mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
454                     lifetimeOffset - lastNonLifetimeStart));
455             return lifetimeOffset + lifetimeLength;
456         }
457 
addNonLifetimeU32(int lastNonLifetimeStart)458         private int addNonLifetimeU32(int lastNonLifetimeStart) {
459             return addNonLifetime(lastNonLifetimeStart,
460                     ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
461         }
462 
463         // Note that this parses RA and may throw IllegalArgumentException (from
464         // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
465         // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
466         // specifications.
Ra(byte[] packet, int length)467         Ra(byte[] packet, int length) throws InvalidRaException {
468             if (length < ICMP6_RA_OPTION_OFFSET) {
469                 throw new InvalidRaException("Not an ICMP6 router advertisement");
470             }
471 
472             mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
473             mLastSeen = currentTimeSeconds();
474 
475             // Sanity check packet in case a packet arrives before we attach RA filter
476             // to our packet socket. b/29586253
477             if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
478                     getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
479                     getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) {
480                 throw new InvalidRaException("Not an ICMP6 router advertisement");
481             }
482 
483 
484             RaEvent.Builder builder = new RaEvent.Builder();
485 
486             // Ignore the flow label and low 4 bits of traffic class.
487             int lastNonLifetimeStart = addNonLifetime(0,
488                     IPV6_FLOW_LABEL_OFFSET,
489                     IPV6_FLOW_LABEL_LEN);
490 
491             // Ignore the checksum.
492             lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
493                     ICMP6_RA_CHECKSUM_OFFSET,
494                     ICMP6_RA_CHECKSUM_LEN);
495 
496             // Parse router lifetime
497             lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
498                     ICMP6_RA_ROUTER_LIFETIME_OFFSET,
499                     ICMP6_RA_ROUTER_LIFETIME_LEN);
500             builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
501 
502             // Ensures that the RA is not truncated.
503             mPacket.position(ICMP6_RA_OPTION_OFFSET);
504             while (mPacket.hasRemaining()) {
505                 final int position = mPacket.position();
506                 final int optionType = getUint8(mPacket, position);
507                 final int optionLength = getUint8(mPacket, position + 1) * 8;
508                 long lifetime;
509                 switch (optionType) {
510                     case ICMP6_PREFIX_OPTION_TYPE:
511                         // Parse valid lifetime
512                         lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
513                                 ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
514                                 ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
515                         lifetime = getUint32(mPacket,
516                                 position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
517                         builder.updatePrefixValidLifetime(lifetime);
518                         // Parse preferred lifetime
519                         lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
520                                 ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
521                                 ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
522                         lifetime = getUint32(mPacket,
523                                 position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
524                         builder.updatePrefixPreferredLifetime(lifetime);
525                         mPrefixOptionOffsets.add(position);
526                         break;
527                     // These three options have the same lifetime offset and size, and
528                     // are processed with the same specialized addNonLifetimeU32:
529                     case ICMP6_RDNSS_OPTION_TYPE:
530                         mRdnssOptionOffsets.add(position);
531                         lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
532                         lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
533                         builder.updateRdnssLifetime(lifetime);
534                         break;
535                     case ICMP6_ROUTE_INFO_OPTION_TYPE:
536                         lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
537                         lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
538                         builder.updateRouteInfoLifetime(lifetime);
539                         break;
540                     case ICMP6_DNSSL_OPTION_TYPE:
541                         lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
542                         lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
543                         builder.updateDnsslLifetime(lifetime);
544                         break;
545                     default:
546                         // RFC4861 section 4.2 dictates we ignore unknown options for fowards
547                         // compatibility.
548                         break;
549                 }
550                 if (optionLength <= 0) {
551                     throw new InvalidRaException(String.format(
552                         "Invalid option length opt=%d len=%d", optionType, optionLength));
553                 }
554                 mPacket.position(position + optionLength);
555             }
556             // Mark non-lifetime bytes since last lifetime.
557             addNonLifetime(lastNonLifetimeStart, 0, 0);
558             mMinLifetime = minLifetime(packet, length);
559             mMetricsLog.log(builder.build());
560         }
561 
562         // Ignoring lifetimes (which may change) does {@code packet} match this RA?
matches(byte[] packet, int length)563         boolean matches(byte[] packet, int length) {
564             if (length != mPacket.capacity()) return false;
565             byte[] referencePacket = mPacket.array();
566             for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
567                 for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) {
568                     if (packet[i] != referencePacket[i]) return false;
569                 }
570             }
571             return true;
572         }
573 
574         // What is the minimum of all lifetimes within {@code packet} in seconds?
575         // Precondition: matches(packet, length) already returned true.
minLifetime(byte[] packet, int length)576         long minLifetime(byte[] packet, int length) {
577             long minLifetime = Long.MAX_VALUE;
578             // Wrap packet in ByteBuffer so we can read big-endian values easily
579             ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
580             for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
581                 int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
582 
583                 // The flow label is in mNonLifetimes, but it's not a lifetime.
584                 if (offset == IPV6_FLOW_LABEL_OFFSET) {
585                     continue;
586                 }
587 
588                 // The checksum is in mNonLifetimes, but it's not a lifetime.
589                 if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
590                     continue;
591                 }
592 
593                 final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
594                 final long optionLifetime;
595                 switch (lifetimeLength) {
596                     case 2:
597                         optionLifetime = getUint16(byteBuffer, offset);
598                         break;
599                     case 4:
600                         optionLifetime = getUint32(byteBuffer, offset);
601                         break;
602                     default:
603                         throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
604                 }
605                 minLifetime = Math.min(minLifetime, optionLifetime);
606             }
607             return minLifetime;
608         }
609 
610         // How many seconds does this RA's have to live, taking into account the fact
611         // that we might have seen it a while ago.
currentLifetime()612         long currentLifetime() {
613             return mMinLifetime - (currentTimeSeconds() - mLastSeen);
614         }
615 
isExpired()616         boolean isExpired() {
617             // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
618             // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
619             return currentLifetime() <= 0;
620         }
621 
622         // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
623         // Jump to the next filter if packet doesn't match this RA.
624         @GuardedBy("ApfFilter.this")
generateFilterLocked(ApfGenerator gen)625         long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
626             String nextFilterLabel = "Ra" + getUniqueNumberLocked();
627             // Skip if packet is not the right size
628             gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
629             gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
630             int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
631             // Skip filter if expired
632             gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
633             gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
634             for (int i = 0; i < mNonLifetimes.size(); i++) {
635                 // Generate code to match the packet bytes
636                 Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
637                 // Don't generate JNEBS instruction for 0 bytes as it always fails the
638                 // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
639                 // the number of bytes to compare. nonLifetime is zero between the
640                 // valid and preferred lifetimes in the prefix option.
641                 if (nonLifetime.second != 0) {
642                     gen.addLoadImmediate(Register.R0, nonLifetime.first);
643                     gen.addJumpIfBytesNotEqual(Register.R0,
644                             Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
645                                                nonLifetime.first + nonLifetime.second),
646                             nextFilterLabel);
647                 }
648                 // Generate code to test the lifetimes haven't gone down too far
649                 if ((i + 1) < mNonLifetimes.size()) {
650                     Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
651                     int offset = nonLifetime.first + nonLifetime.second;
652 
653                     // Skip the Flow label.
654                     if (offset == IPV6_FLOW_LABEL_OFFSET) {
655                         continue;
656                     }
657                     // Skip the checksum.
658                     if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
659                         continue;
660                     }
661                     int length = nextNonLifetime.first - offset;
662                     switch (length) {
663                         case 4: gen.addLoad32(Register.R0, offset); break;
664                         case 2: gen.addLoad16(Register.R0, offset); break;
665                         default: throw new IllegalStateException("bogus lifetime size " + length);
666                     }
667                     gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
668                 }
669             }
670             gen.addJump(gen.DROP_LABEL);
671             gen.defineLabel(nextFilterLabel);
672             return filterLifetime;
673         }
674     }
675 
676     // Maximum number of RAs to filter for.
677     private static final int MAX_RAS = 10;
678 
679     @GuardedBy("this")
680     private ArrayList<Ra> mRas = new ArrayList<Ra>();
681 
682     // There is always some marginal benefit to updating the installed APF program when an RA is
683     // seen because we can extend the program's lifetime slightly, but there is some cost to
684     // updating the program, so don't bother unless the program is going to expire soon. This
685     // constant defines "soon" in seconds.
686     private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
687     // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
688     // see a refresh.  Using half the lifetime might be a good idea except for the fact that
689     // packets may be dropped, so let's use 6.
690     private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
691 
692     // When did we last install a filter program? In seconds since Unix Epoch.
693     @GuardedBy("this")
694     private long mLastTimeInstalledProgram;
695     // How long should the last installed filter program live for? In seconds.
696     @GuardedBy("this")
697     private long mLastInstalledProgramMinLifetime;
698     @GuardedBy("this")
699     private ApfProgramEvent mLastInstallEvent;
700 
701     // For debugging only. The last program installed.
702     @GuardedBy("this")
703     private byte[] mLastInstalledProgram;
704 
705     // How many times the program was updated since we started.
706     @GuardedBy("this")
707     private int mNumProgramUpdates = 0;
708     // How many times the program was updated since we started for allowing multicast traffic.
709     @GuardedBy("this")
710     private int mNumProgramUpdatesAllowingMulticast = 0;
711 
712     /**
713      * Generate filter code to process ARP packets. Execution of this code ends in either the
714      * DROP_LABEL or PASS_LABEL and does not fall off the end.
715      * Preconditions:
716      *  - Packet being filtered is ARP
717      */
718     @GuardedBy("this")
generateArpFilterLocked(ApfGenerator gen)719     private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
720         // Here's a basic summary of what the ARP filter program does:
721         //
722         // if not ARP IPv4
723         //   pass
724         // if not ARP IPv4 reply or request
725         //   pass
726         // if unicast ARP reply
727         //   pass
728         // if interface has no IPv4 address
729         //   if target ip is 0.0.0.0
730         //      drop
731         // else
732         //   if target ip is not the interface ip
733         //      drop
734         // pass
735 
736         final String checkTargetIPv4 = "checkTargetIPv4";
737 
738         // Pass if not ARP IPv4.
739         gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
740         gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, gen.PASS_LABEL);
741 
742         // Pass if unknown ARP opcode.
743         gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
744         gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
745         gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, gen.PASS_LABEL);
746 
747         // Pass if unicast reply.
748         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
749         gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
750 
751         // Either a unicast request, a unicast reply, or a broadcast reply.
752         gen.defineLabel(checkTargetIPv4);
753         if (mIPv4Address == null) {
754             // When there is no IPv4 address, drop GARP replies (b/29404209).
755             gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
756             gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, gen.DROP_LABEL);
757         } else {
758             // When there is an IPv4 address, drop unicast/broadcast requests
759             // and broadcast replies with a different target IPv4 address.
760             gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
761             gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
762         }
763 
764         gen.addJump(gen.PASS_LABEL);
765     }
766 
767     /**
768      * Generate filter code to process IPv4 packets. Execution of this code ends in either the
769      * DROP_LABEL or PASS_LABEL and does not fall off the end.
770      * Preconditions:
771      *  - Packet being filtered is IPv4
772      */
773     @GuardedBy("this")
generateIPv4FilterLocked(ApfGenerator gen)774     private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
775         // Here's a basic summary of what the IPv4 filter program does:
776         //
777         // if filtering multicast (i.e. multicast lock not held):
778         //   if it's DHCP destined to our MAC:
779         //     pass
780         //   if it's L2 broadcast:
781         //     drop
782         //   if it's IPv4 multicast:
783         //     drop
784         //   if it's IPv4 broadcast:
785         //     drop
786         // pass
787 
788         if (mMulticastFilter) {
789             final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
790 
791             // Pass DHCP addressed to us.
792             // Check it's UDP.
793             gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
794             gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
795             // Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
796             gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
797             gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
798             // Check it's addressed to DHCP client port.
799             gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
800             gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
801             gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
802             // Check it's DHCP to our MAC address.
803             gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
804             // NOTE: Relies on R1 containing IPv4 header offset.
805             gen.addAddR1();
806             gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
807             gen.addJump(gen.PASS_LABEL);
808 
809             // Drop all multicasts/broadcasts.
810             gen.defineLabel(skipDhcpv4Filter);
811 
812             // If IPv4 destination address is in multicast range, drop.
813             gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
814             gen.addAnd(0xf0);
815             gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
816 
817             // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
818             gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
819             gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, gen.DROP_LABEL);
820             if (mIPv4Address != null && mIPv4PrefixLength < 31) {
821                 int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
822                 gen.addJumpIfR0Equals(broadcastAddr, gen.DROP_LABEL);
823             }
824 
825             // If L2 broadcast packet, drop.
826             gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
827             gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
828             gen.addJump(gen.DROP_LABEL);
829         }
830 
831         // Otherwise, pass
832         gen.addJump(gen.PASS_LABEL);
833     }
834 
835 
836     /**
837      * Generate filter code to process IPv6 packets. Execution of this code ends in either the
838      * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
839      * Preconditions:
840      *  - Packet being filtered is IPv6
841      */
842     @GuardedBy("this")
generateIPv6FilterLocked(ApfGenerator gen)843     private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
844         // Here's a basic summary of what the IPv6 filter program does:
845         //
846         // if it's not ICMPv6:
847         //   if it's multicast and we're dropping multicast:
848         //     drop
849         //   pass
850         // if it's ICMPv6 RS to any:
851         //   drop
852         // if it's ICMPv6 NA to ff02::1:
853         //   drop
854 
855         gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
856 
857         // Drop multicast if the multicast filter is enabled.
858         if (mMulticastFilter) {
859             // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
860             String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
861             gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
862 
863             // Drop all other packets sent to ff00::/8.
864             gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
865             gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
866             // Not multicast and not ICMPv6. Pass.
867             gen.addJump(gen.PASS_LABEL);
868             gen.defineLabel(skipIpv6MulticastFilterLabel);
869         } else {
870             // If not ICMPv6, pass.
871             gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
872         }
873 
874         // Add unsolicited multicast neighbor announcements filter
875         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
876         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
877         // Drop all router solicitations (b/32833400)
878         gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL);
879         // If not neighbor announcements, skip filter.
880         gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
881         // If to ff02::1, drop.
882         // TODO: Drop only if they don't contain the address of on-link neighbours.
883         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
884         gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
885                 skipUnsolicitedMulticastNALabel);
886         gen.addJump(gen.DROP_LABEL);
887         gen.defineLabel(skipUnsolicitedMulticastNALabel);
888     }
889 
890     /**
891      * Begin generating an APF program to:
892      * <ul>
893      * <li>Drop/Pass 802.3 frames (based on policy)
894      * <li>Drop ARP requests not for us, if mIPv4Address is set,
895      * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
896      * <li>Drop IPv4 multicast packets, if mMulticastFilter,
897      * <li>Pass all other IPv4 packets,
898      * <li>Drop all broadcast non-IP non-ARP packets.
899      * <li>Pass all non-ICMPv6 IPv6 packets,
900      * <li>Pass all non-IPv4 and non-IPv6 packets,
901      * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
902      * <li>Drop IPv6 ICMPv6 RSs.
903      * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
904      *     insertion of RA filters here, or if there aren't any, just passes the packets.
905      * </ul>
906      */
907     @GuardedBy("this")
beginProgramLocked()908     private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
909         ApfGenerator gen = new ApfGenerator();
910         // This is guaranteed to return true because of the check in maybeCreate.
911         gen.setApfVersion(mApfCapabilities.apfVersionSupported);
912 
913         // Here's a basic summary of what the initial program does:
914         //
915         // if it's a 802.3 Frame (ethtype < 0x0600):
916         //    drop or pass based on configurations
917         // if it's ARP:
918         //   insert ARP filter to drop or pass these appropriately
919         // if it's IPv4:
920         //   insert IPv4 filter to drop or pass these appropriately
921         // if it's not IPv6:
922         //   if it's broadcast:
923         //     drop
924         //   pass
925         // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
926 
927         gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
928 
929         if (mDrop802_3Frames) {
930             // drop 802.3 frames (ethtype < 0x0600)
931             gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL);
932         }
933 
934         // Add ARP filters:
935         String skipArpFiltersLabel = "skipArpFilters";
936         gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
937         generateArpFilterLocked(gen);
938         gen.defineLabel(skipArpFiltersLabel);
939 
940         // Add IPv4 filters:
941         String skipIPv4FiltersLabel = "skipIPv4Filters";
942         // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
943         // execute the ARP filter, since that filter does not fall through, but either drops or
944         // passes.
945         gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
946         generateIPv4FilterLocked(gen);
947         gen.defineLabel(skipIPv4FiltersLabel);
948 
949         // Check for IPv6:
950         // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
951         // execute the ARP or IPv4 filters, since those filters do not fall through, but either
952         // drop or pass.
953         String ipv6FilterLabel = "IPv6Filters";
954         gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel);
955 
956         // Drop non-IP non-ARP broadcasts, pass the rest
957         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
958         gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
959         gen.addJump(gen.DROP_LABEL);
960 
961         // Add IPv6 filters:
962         gen.defineLabel(ipv6FilterLabel);
963         generateIPv6FilterLocked(gen);
964         return gen;
965     }
966 
967     /**
968      * Generate and install a new filter program.
969      */
970     @GuardedBy("this")
971     @VisibleForTesting
installNewProgramLocked()972     void installNewProgramLocked() {
973         purgeExpiredRasLocked();
974         ArrayList<Ra> rasToFilter = new ArrayList<>();
975         final byte[] program;
976         long programMinLifetime = Long.MAX_VALUE;
977         try {
978             // Step 1: Determine how many RA filters we can fit in the program.
979             ApfGenerator gen = beginProgramLocked();
980             for (Ra ra : mRas) {
981                 ra.generateFilterLocked(gen);
982                 // Stop if we get too big.
983                 if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
984                 rasToFilter.add(ra);
985             }
986             // Step 2: Actually generate the program
987             gen = beginProgramLocked();
988             for (Ra ra : rasToFilter) {
989                 programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
990             }
991             // Execution will reach the end of the program if no filters match, which will pass the
992             // packet to the AP.
993             program = gen.generate();
994         } catch (IllegalInstructionException|IllegalStateException e) {
995             Log.e(TAG, "Failed to generate APF program.", e);
996             return;
997         }
998         final long now = currentTimeSeconds();
999         mLastTimeInstalledProgram = now;
1000         mLastInstalledProgramMinLifetime = programMinLifetime;
1001         mLastInstalledProgram = program;
1002         mNumProgramUpdates++;
1003 
1004         if (VDBG) {
1005             hexDump("Installing filter: ", program, program.length);
1006         }
1007         mIpManagerCallback.installPacketFilter(program);
1008         logApfProgramEventLocked(now);
1009         mLastInstallEvent = new ApfProgramEvent();
1010         mLastInstallEvent.lifetime = programMinLifetime;
1011         mLastInstallEvent.filteredRas = rasToFilter.size();
1012         mLastInstallEvent.currentRas = mRas.size();
1013         mLastInstallEvent.programLength = program.length;
1014         mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
1015     }
1016 
logApfProgramEventLocked(long now)1017     private void logApfProgramEventLocked(long now) {
1018         if (mLastInstallEvent == null) {
1019             return;
1020         }
1021         ApfProgramEvent ev = mLastInstallEvent;
1022         mLastInstallEvent = null;
1023         ev.actualLifetime = now - mLastTimeInstalledProgram;
1024         if (ev.actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
1025             return;
1026         }
1027         mMetricsLog.log(ev);
1028     }
1029 
1030     /**
1031      * Returns {@code true} if a new program should be installed because the current one dies soon.
1032      */
shouldInstallnewProgram()1033     private boolean shouldInstallnewProgram() {
1034         long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
1035         return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
1036     }
1037 
hexDump(String msg, byte[] packet, int length)1038     private void hexDump(String msg, byte[] packet, int length) {
1039         log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
1040     }
1041 
1042     @GuardedBy("this")
purgeExpiredRasLocked()1043     private void purgeExpiredRasLocked() {
1044         for (int i = 0; i < mRas.size();) {
1045             if (mRas.get(i).isExpired()) {
1046                 log("Expiring " + mRas.get(i));
1047                 mRas.remove(i);
1048             } else {
1049                 i++;
1050             }
1051         }
1052     }
1053 
1054     /**
1055      * Process an RA packet, updating the list of known RAs and installing a new APF program
1056      * if the current APF program should be updated.
1057      * @return a ProcessRaResult enum describing what action was performed.
1058      */
1059     @VisibleForTesting
processRa(byte[] packet, int length)1060     synchronized ProcessRaResult processRa(byte[] packet, int length) {
1061         if (VDBG) hexDump("Read packet = ", packet, length);
1062 
1063         // Have we seen this RA before?
1064         for (int i = 0; i < mRas.size(); i++) {
1065             Ra ra = mRas.get(i);
1066             if (ra.matches(packet, length)) {
1067                 if (VDBG) log("matched RA " + ra);
1068                 // Update lifetimes.
1069                 ra.mLastSeen = currentTimeSeconds();
1070                 ra.mMinLifetime = ra.minLifetime(packet, length);
1071                 ra.seenCount++;
1072 
1073                 // Keep mRas in LRU order so as to prioritize generating filters for recently seen
1074                 // RAs. LRU prioritizes this because RA filters are generated in order from mRas
1075                 // until the filter program exceeds the maximum filter program size allowed by the
1076                 // chipset, so RAs appearing earlier in mRas are more likely to make it into the
1077                 // filter program.
1078                 // TODO: consider sorting the RAs in order of increasing expiry time as well.
1079                 // Swap to front of array.
1080                 mRas.add(0, mRas.remove(i));
1081 
1082                 // If the current program doesn't expire for a while, don't update.
1083                 if (shouldInstallnewProgram()) {
1084                     installNewProgramLocked();
1085                     return ProcessRaResult.UPDATE_EXPIRY;
1086                 }
1087                 return ProcessRaResult.MATCH;
1088             }
1089         }
1090         purgeExpiredRasLocked();
1091         // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
1092         if (mRas.size() >= MAX_RAS) {
1093             return ProcessRaResult.DROPPED;
1094         }
1095         final Ra ra;
1096         try {
1097             ra = new Ra(packet, length);
1098         } catch (Exception e) {
1099             Log.e(TAG, "Error parsing RA", e);
1100             return ProcessRaResult.PARSE_ERROR;
1101         }
1102         // Ignore 0 lifetime RAs.
1103         if (ra.isExpired()) {
1104             return ProcessRaResult.ZERO_LIFETIME;
1105         }
1106         log("Adding " + ra);
1107         mRas.add(ra);
1108         installNewProgramLocked();
1109         return ProcessRaResult.UPDATE_NEW_RA;
1110     }
1111 
1112     /**
1113      * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
1114      * filtering using APF programs.
1115      */
maybeCreate(ApfCapabilities apfCapabilities, NetworkInterface networkInterface, IpManager.Callback ipManagerCallback, boolean multicastFilter, boolean ieee802_3Filter)1116     public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
1117             NetworkInterface networkInterface, IpManager.Callback ipManagerCallback,
1118             boolean multicastFilter, boolean ieee802_3Filter) {
1119         if (apfCapabilities == null || networkInterface == null) return null;
1120         if (apfCapabilities.apfVersionSupported == 0) return null;
1121         if (apfCapabilities.maximumApfProgramSize < 512) {
1122             Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
1123             return null;
1124         }
1125         // For now only support generating programs for Ethernet frames. If this restriction is
1126         // lifted:
1127         //   1. the program generator will need its offsets adjusted.
1128         //   2. the packet filter attached to our packet socket will need its offset adjusted.
1129         if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
1130         if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
1131             Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
1132             return null;
1133         }
1134         return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
1135                 multicastFilter, ieee802_3Filter, new IpConnectivityLog());
1136     }
1137 
shutdown()1138     public synchronized void shutdown() {
1139         if (mReceiveThread != null) {
1140             log("shutting down");
1141             mReceiveThread.halt();  // Also closes socket.
1142             mReceiveThread = null;
1143         }
1144         mRas.clear();
1145     }
1146 
setMulticastFilter(boolean isEnabled)1147     public synchronized void setMulticastFilter(boolean isEnabled) {
1148         if (mMulticastFilter == isEnabled) {
1149             return;
1150         }
1151         mMulticastFilter = isEnabled;
1152         if (!isEnabled) {
1153             mNumProgramUpdatesAllowingMulticast++;
1154         }
1155         installNewProgramLocked();
1156     }
1157 
1158     /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
findIPv4LinkAddress(LinkProperties lp)1159     private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
1160         LinkAddress ipv4Address = null;
1161         for (LinkAddress address : lp.getLinkAddresses()) {
1162             if (!(address.getAddress() instanceof Inet4Address)) {
1163                 continue;
1164             }
1165             if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
1166                 // More than one IPv4 address, abort.
1167                 return null;
1168             }
1169             ipv4Address = address;
1170         }
1171         return ipv4Address;
1172     }
1173 
setLinkProperties(LinkProperties lp)1174     public synchronized void setLinkProperties(LinkProperties lp) {
1175         // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
1176         final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
1177         final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
1178         final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
1179         if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
1180             return;
1181         }
1182         mIPv4Address = addr;
1183         mIPv4PrefixLength = prefix;
1184         installNewProgramLocked();
1185     }
1186 
dump(IndentingPrintWriter pw)1187     public synchronized void dump(IndentingPrintWriter pw) {
1188         pw.println("Capabilities: " + mApfCapabilities);
1189         pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
1190         pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW"));
1191         try {
1192             pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress());
1193         } catch (UnknownHostException|NullPointerException e) {}
1194 
1195         if (mLastTimeInstalledProgram == 0) {
1196             pw.println("No program installed.");
1197             return;
1198         }
1199         pw.println("Program updates: " + mNumProgramUpdates);
1200         pw.println(String.format(
1201                 "Last program length %d, installed %ds ago, lifetime %ds",
1202                 mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram,
1203                 mLastInstalledProgramMinLifetime));
1204 
1205         pw.println("RA filters:");
1206         pw.increaseIndent();
1207         for (Ra ra: mRas) {
1208             pw.println(ra);
1209             pw.increaseIndent();
1210             pw.println(String.format(
1211                     "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen));
1212             if (DBG) {
1213                 pw.println("Last match:");
1214                 pw.increaseIndent();
1215                 pw.println(ra.getLastMatchingPacket());
1216                 pw.decreaseIndent();
1217             }
1218             pw.decreaseIndent();
1219         }
1220         pw.decreaseIndent();
1221 
1222         if (DBG) {
1223             pw.println("Last program:");
1224             pw.increaseIndent();
1225             pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
1226             pw.decreaseIndent();
1227         }
1228     }
1229 
1230     // TODO: move to android.net.NetworkUtils
1231     @VisibleForTesting
ipv4BroadcastAddress(byte[] addrBytes, int prefixLength)1232     public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
1233         return bytesToBEInt(addrBytes) | (int) (uint32(-1) >>> prefixLength);
1234     }
1235 }
1236