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