1 /*
2 * Copyright (C) 2017 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 #include "interface.h"
18
19 #include <errno.h>
20 #include <linux/if_ether.h>
21 #include <net/if.h>
22 #include <string.h>
23 #include <sys/ioctl.h>
24
25 #include "log.h"
26
Interface(const std::string & name)27 Interface::Interface(const std::string& name) : mName(name) {
28 }
29
init()30 bool Interface::init() {
31 // setAllMulti will set the ALLMULTI flag for the interface, this allows us
32 // to capture all the traffic needed to perform proxying.
33 return setAllMulti() &&
34 resolveAddresses() &&
35 configureIpSocket() &&
36 configureIcmpSocket();
37 }
38
setAllMulti()39 bool Interface::setAllMulti() {
40 struct ifreq request;
41 memset(&request, 0, sizeof(request));
42 strncpy(request.ifr_name, mName.c_str(), sizeof(request.ifr_name));
43 request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
44
45 Socket socket;
46 Result res = socket.open(AF_INET, SOCK_DGRAM, IPPROTO_IP);
47 if (!res) {
48 loge("Failed to open IP socket for interface %s: %s\n",
49 mName.c_str(), strerror(errno));
50 return false;
51 }
52 int status = ::ioctl(socket.get(), SIOCGIFFLAGS, &request);
53 if (status != 0) {
54 loge("Failed to get interface flags for %s: %s\n",
55 mName.c_str(), strerror(errno));
56 return false;
57 }
58
59 if ((request.ifr_flags & IFF_ALLMULTI) != 0) {
60 // AllMulti is already enabled, nothing to do
61 return true;
62 }
63
64 request.ifr_flags |= IFF_ALLMULTI;
65
66 status = ::ioctl(socket.get(), SIOCSIFFLAGS, &request);
67 if (status != 0) {
68 loge("Failed to enable AllMulti flag for %s: %s\n",
69 mName.c_str(), strerror(errno));
70 return false;
71 }
72 return true;
73 }
74
resolveAddresses()75 bool Interface::resolveAddresses() {
76 Result res = mLinkAddr.resolveEth(mName);
77 if (!res) {
78 loge("Unable to resolve interface %s: %s\n",
79 mName.c_str(), res.c_str());
80 return false;
81 }
82 mIndex = if_nametoindex(mName.c_str());
83 if (mIndex == 0) {
84 loge("Unable to get interface index for '%s': %s\n",
85 mName.c_str(), strerror(errno));
86 return false;
87 }
88 return true;
89 }
90
configureIcmpSocket()91 bool Interface::configureIcmpSocket() {
92 Result res = mIcmpSocket.open(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
93 if (!res) {
94 loge("Error opening socket: %s\n", res.c_str());
95 return false;
96 }
97
98 // The ICMP messages we are going to send need a hop limit of 255 to be
99 // accepted.
100 res = mIcmpSocket.setMulticastHopLimit(255);
101 if (!res) {
102 loge("Error setting socket hop limit: %s\n", res.c_str());
103 return false;
104 }
105 res = mIcmpSocket.setUnicastHopLimit(255);
106 if (!res) {
107 loge("Error setting socket hop limit: %s\n", res.c_str());
108 return false;
109 }
110
111 // We only care about one specific interface
112 res = mIcmpSocket.setInterface(mName);
113 if (!res) {
114 loge("Error socket interface: %s\n", res.c_str());
115 return false;
116 }
117
118 // Make sure the socket allows transparent proxying, this allows sending of
119 // packets with a source address that is different from the interface.
120 res = mIcmpSocket.setTransparent(true);
121 if (!res) {
122 loge("Error socket interface: %s\n", res.c_str());
123 return false;
124 }
125
126 return true;
127 }
128
configureIpSocket()129 bool Interface::configureIpSocket() {
130 Result res = mIpSocket.open(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
131 if (!res) {
132 loge("Error opening socket: %s\n", res.c_str());
133 return false;
134 }
135
136 res = mIpSocket.bind(mLinkAddr);
137 if (!res) {
138 loge("Error binding socket: %s\n", res.c_str());
139 return false;
140 }
141 return true;
142 }
143
144