Home | History | Annotate | Download | only in ipv6proxy
      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 "proxy.h"
     18 
     19 #include <errno.h>
     20 #include <linux/if_packet.h>
     21 #include <poll.h>
     22 #include <signal.h>
     23 
     24 #include "log.h"
     25 #include "message.h"
     26 #include "packet.h"
     27 #include "result.h"
     28 
     29 // The prefix length for an address of a single unique node
     30 static const uint8_t kNodePrefixLength = 128;
     31 static const size_t kLinkAddressSize = 6;
     32 
     33 // Rewrite the link address of a neighbor discovery option to the link address
     34 // of |interface|. This can be either a source or target link address as
     35 // specified by |optionType|. The valid values are ND_OPT_TARGET_LINKADDR and
     36 // ND_OPT_SOURCE_LINKADDR. This will modify the message data inside |packet|.
     37 static void rewriteLinkAddressOption(Packet& packet,
     38                                      const Interface& interface,
     39                                      int optionType) {
     40     for (nd_opt_hdr* opt = packet.firstOpt(); opt; opt = packet.nextOpt(opt)) {
     41         if (opt->nd_opt_type == optionType) {
     42             auto src = interface.linkAddr().get<sockaddr_ll>();
     43             auto dest = reinterpret_cast<char*>(opt) + sizeof(nd_opt_hdr);
     44             memcpy(dest, src->sll_addr, kLinkAddressSize);
     45         }
     46     }
     47 }
     48 
     49 int Proxy::run() {
     50     sigset_t blockMask, originalMask;
     51     int status = ::sigfillset(&blockMask);
     52     if (status != 0) {
     53         loge("Unable to fill signal set: %s\n", strerror(errno));
     54         return 1;
     55     }
     56     status = ::sigprocmask(SIG_SETMASK, &blockMask, &originalMask);
     57     if (status != 0) {
     58         loge("Unable to set signal mask: %s\n", strerror(errno));
     59         return 1;
     60     }
     61     // Init outer interface and router
     62     if (!mOuterIf.init() || !mRouter.init()) {
     63         return 1;
     64     }
     65     // Init all inner interfaces
     66     for (size_t i = 0; i < mInnerIfs.size(); ++i) {
     67         if (!mInnerIfs[i].init()) {
     68             return 1;
     69         }
     70     }
     71 
     72     // Create list of FDs to poll, we're only looking for input (POLLIN)
     73     std::vector<pollfd> fds(mInnerIfs.size() + 1);
     74     fds[0].fd = mOuterIf.ipSocket().get();
     75     fds[0].events = POLLIN;
     76     for (size_t i = 0; i < mInnerIfs.size(); ++i) {
     77         fds[i + 1].fd = mInnerIfs[i].ipSocket().get();
     78         fds[i + 1].events = POLLIN;
     79     }
     80 
     81     Message message;
     82     while (status >= 0) {
     83         status = ::ppoll(fds.data(), fds.size(), nullptr, &originalMask);
     84         if (status > 0) {
     85             // Something available to read
     86             for (const struct pollfd& fd : fds) {
     87                 if (receiveIfPossible(fd, mOuterIf.ipSocket(), &message)) {
     88                     // Received a message on the outer interface
     89                     handleOuterMessage(message);
     90                 } else {
     91                     for (auto& inner : mInnerIfs) {
     92                         if (receiveIfPossible(fd, inner.ipSocket(), &message)) {
     93                             // Received a message on the inner interface
     94                             handleInnerMessage(inner, message);
     95                         }
     96                     }
     97                 }
     98             }
     99         }
    100     }
    101     loge("Polling failed: %s\n", strerror(errno));
    102     return 1;
    103 }
    104 
    105 bool Proxy::receiveIfPossible(const pollfd& fd,
    106                               Socket& socket,
    107                               Message* message) {
    108     // Check if it's actually the socket we're interested in
    109     if (fd.fd != socket.get()) {
    110         return false;
    111     }
    112     // Check if there is something to read on this socket
    113     if ((fd.revents & POLLIN) == 0) {
    114         return false;
    115     }
    116 
    117     // Receive the message and place the data in the message parameter
    118     Result res = socket.receive(message);
    119     if (!res) {
    120         loge("Error receiving on socket: %s\n", res.c_str());
    121         return false;
    122     }
    123     return true;
    124 }
    125 
    126 void Proxy::handleOuterMessage(Message& message) {
    127     Packet packet(message);
    128     uint32_t options = kForwardOnly;
    129     switch (packet.type()) {
    130         case Packet::Type::RouterAdvertisement:
    131             options = kRewriteSourceLink | kSetDefaultGateway;
    132             break;
    133         case Packet::Type::NeighborSolicitation:
    134             options = kSpoofSource;
    135             break;
    136         case Packet::Type::NeighborAdvertisement:
    137             options = kRewriteTargetLink;
    138             break;
    139         default:
    140             return;
    141     }
    142     for (auto& inner : mInnerIfs) {
    143         forward(mOuterIf, inner, packet, options);
    144     }
    145 }
    146 
    147 void Proxy::handleInnerMessage(const Interface& inner, Message& message) {
    148     Packet packet(message);
    149     uint32_t options = kForwardOnly;
    150     switch (packet.type()) {
    151         case Packet::Type::RouterSolicitation:
    152             options = kSpoofSource;
    153             break;
    154         case Packet::Type::NeighborSolicitation:
    155             options = kSpoofSource | kAddRoute;
    156             break;
    157         case Packet::Type::NeighborAdvertisement:
    158             options = kRewriteTargetLink | kSpoofSource | kAddRoute;
    159             break;
    160         default:
    161             return;
    162     }
    163     forward(inner, mOuterIf, packet, options);
    164 }
    165 
    166 void Proxy::forward(const Interface& from,
    167                     Interface& to,
    168                     Packet& packet,
    169                     uint32_t options) {
    170     if (mLogDebug) {
    171         logd("Forwarding %s from %s/%s to %s/%s\n",
    172              packet.description().c_str(),
    173              from.name().c_str(), addrToStr(packet.ip()->ip6_src).c_str(),
    174              to.name().c_str(), addrToStr(packet.ip()->ip6_dst).c_str());
    175     }
    176 
    177     if (options & kRewriteTargetLink) {
    178         rewriteLinkAddressOption(packet, to, ND_OPT_TARGET_LINKADDR);
    179     }
    180     if (options & kRewriteSourceLink) {
    181         rewriteLinkAddressOption(packet, to, ND_OPT_SOURCE_LINKADDR);
    182     }
    183 
    184     Result res = Result::success();
    185     if (options & kSpoofSource) {
    186         // Spoof the source of the packet so that it appears to originate from
    187         // the same source that we see.
    188         res = to.icmpSocket().sendFrom(packet.ip()->ip6_src,
    189                                        packet.ip()->ip6_dst,
    190                                        packet.icmp(),
    191                                        packet.icmpSize());
    192     } else {
    193         res = to.icmpSocket().sendTo(packet.ip()->ip6_dst,
    194                                      packet.icmp(),
    195                                      packet.icmpSize());
    196     }
    197     if (!res) {
    198         loge("Failed to forward %s from %s to %s: %s\n",
    199              packet.description().c_str(),
    200              from.name().c_str(), to.name().c_str(),
    201              res.c_str());
    202     }
    203 
    204     if (options & kAddRoute) {
    205         mRouter.addRoute(packet.ip()->ip6_src, kNodePrefixLength, from.index());
    206     }
    207     if (packet.type() == Packet::Type::RouterAdvertisement &&
    208         options & kSetDefaultGateway) {
    209         // Set the default gateway from this router advertisement. This is
    210         // needed so that packets that are forwarded as a result of proxying
    211         // actually have somewhere to go.
    212         if (!mRouter.setDefaultGateway(packet.ip()->ip6_src, from.index())) {
    213             loge("Failed to set default gateway %s\n",
    214                  addrToStr(packet.ip()->ip6_src).c_str());
    215         }
    216     }
    217 }
    218 
    219