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