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