• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.ip;
18 
19 import static android.system.OsConstants.AF_INET6;
20 import static android.system.OsConstants.AF_PACKET;
21 import static android.system.OsConstants.ETH_P_IPV6;
22 import static android.system.OsConstants.IPPROTO_RAW;
23 import static android.system.OsConstants.SOCK_DGRAM;
24 import static android.system.OsConstants.SOCK_NONBLOCK;
25 import static android.system.OsConstants.SOCK_RAW;
26 
27 import android.net.util.InterfaceParams;
28 import android.net.util.SocketUtils;
29 import android.net.util.TetheringUtils;
30 import android.os.Handler;
31 import android.system.ErrnoException;
32 import android.system.Os;
33 import android.util.Log;
34 
35 import com.android.net.module.util.PacketReader;
36 
37 import java.io.FileDescriptor;
38 import java.io.IOException;
39 import java.net.Inet6Address;
40 import java.net.InetSocketAddress;
41 import java.net.SocketAddress;
42 import java.net.SocketException;
43 import java.net.UnknownHostException;
44 import java.util.Arrays;
45 
46 /**
47  * Basic IPv6 Neighbor Advertisement Forwarder.
48  *
49  * Forward NA packets from upstream iface to tethered iface
50  * and NS packets from tethered iface to upstream iface.
51  *
52  * @hide
53  */
54 public class NeighborPacketForwarder extends PacketReader {
55     private final String mTag;
56 
57     private FileDescriptor mFd;
58 
59     // TODO: get these from NetworkStackConstants.
60     private static final int IPV6_ADDR_LEN = 16;
61     private static final int IPV6_DST_ADDR_OFFSET = 24;
62     private static final int IPV6_HEADER_LEN = 40;
63     private static final int ETH_HEADER_LEN = 14;
64 
65     private InterfaceParams mListenIfaceParams, mSendIfaceParams;
66 
67     private final int mType;
68     public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT  = 136;
69     public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
70 
NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type)71     public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) {
72         super(h);
73         mTag = NeighborPacketForwarder.class.getSimpleName() + "-"
74                 + tetheredInterface.name + "-" + type;
75         mType = type;
76 
77         if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
78             mSendIfaceParams = tetheredInterface;
79         } else {
80             mListenIfaceParams = tetheredInterface;
81         }
82     }
83 
84     /** Set new upstream iface and start/stop based on new params. */
setUpstreamIface(InterfaceParams upstreamParams)85     public void setUpstreamIface(InterfaceParams upstreamParams) {
86         final InterfaceParams oldUpstreamParams;
87 
88         if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
89             oldUpstreamParams = mListenIfaceParams;
90             mListenIfaceParams = upstreamParams;
91         } else {
92             oldUpstreamParams = mSendIfaceParams;
93             mSendIfaceParams = upstreamParams;
94         }
95 
96         if (oldUpstreamParams == null && upstreamParams != null) {
97             start();
98         } else if (oldUpstreamParams != null && upstreamParams == null) {
99             stop();
100         } else if (oldUpstreamParams != null && upstreamParams != null
101                    && oldUpstreamParams.index != upstreamParams.index) {
102             stop();
103             start();
104         }
105     }
106 
107     // TODO: move NetworkStackUtils.closeSocketQuietly to
108     // frameworks/libs/net/common/device/com/android/net/module/util/[someclass].
closeSocketQuietly(FileDescriptor fd)109     private void closeSocketQuietly(FileDescriptor fd) {
110         try {
111             SocketUtils.closeSocket(fd);
112         } catch (IOException ignored) {
113         }
114     }
115 
116     @Override
createFd()117     protected FileDescriptor createFd() {
118         try {
119             // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used.
120             // To keep uniformity in both directions PACKET socket can be used.
121             mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
122 
123             // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type?
124             if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
125                 TetheringUtils.setupNaSocket(mFd);
126             } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) {
127                 TetheringUtils.setupNsSocket(mFd);
128             }
129 
130             SocketAddress bindAddress = SocketUtils.makePacketSocketAddress(
131                                                         ETH_P_IPV6, mListenIfaceParams.index);
132             Os.bind(mFd, bindAddress);
133         } catch (ErrnoException | SocketException e) {
134             Log.wtf(mTag, "Failed to create  socket", e);
135             closeSocketQuietly(mFd);
136             return null;
137         }
138 
139         return mFd;
140     }
141 
getIpv6DestinationAddress(byte[] recvbuf)142     private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) {
143         Inet6Address dstAddr;
144         try {
145             dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf,
146                     IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN));
147         } catch (UnknownHostException | ClassCastException impossible) {
148             throw new AssertionError("16-byte array not valid IPv6 address?");
149         }
150         return dstAddr;
151     }
152 
153     @Override
handlePacket(byte[] recvbuf, int length)154     protected void handlePacket(byte[] recvbuf, int length) {
155         if (mSendIfaceParams == null) {
156             return;
157         }
158 
159         // The BPF filter should already have checked the length of the packet, but...
160         if (length < IPV6_HEADER_LEN) {
161             return;
162         }
163         Inet6Address destv6 = getIpv6DestinationAddress(recvbuf);
164         if (!destv6.isMulticastAddress()) {
165             return;
166         }
167         InetSocketAddress dest = new InetSocketAddress(destv6, 0);
168 
169         FileDescriptor fd = null;
170         try {
171             fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
172             SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name);
173 
174             int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest);
175         } catch (ErrnoException | SocketException e) {
176             Log.e(mTag, "handlePacket error: " + e);
177         } finally {
178             closeSocketQuietly(fd);
179         }
180     }
181 }
182