/* * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bridge.h" #include "log.h" #include #include #include #include #include #include #include Bridge::Bridge(const std::string& bridgeName) : mBridgeName(bridgeName) { } Bridge::~Bridge() { if (mSocketFd != -1) { ::close(mSocketFd); mSocketFd = -1; } } Result Bridge::init() { Result res = createSocket(); if (!res) { return res; } res = createBridge(); if (!res) { return res; } return Result::success(); } Result Bridge::addInterface(const std::string& interfaceName) { return doInterfaceOperation(interfaceName, SIOCBRADDIF, "add"); } Result Bridge::removeInterface(const std::string& interfaceName) { return doInterfaceOperation(interfaceName, SIOCBRDELIF, "remove"); } Result Bridge::createSocket() { if (mSocketFd != -1) { return Result::error("Bridge already initialized"); } mSocketFd = ::socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (mSocketFd < 0) { return Result::error("Unable to create socket for bridge: %s", strerror(errno)); } return Result::success(); } Result Bridge::createBridge() { int res = ::ioctl(mSocketFd, SIOCBRADDBR, mBridgeName.c_str()); if (res < 0) { // If the bridge already exists we just keep going, that's fine. // Otherwise something went wrong. if (errno != EEXIST) { return Result::error("Cannot create bridge %s: %s", mBridgeName.c_str(), strerror(errno)); } } struct ifreq request; memset(&request, 0, sizeof(request)); // Determine interface index of bridge request.ifr_ifindex = if_nametoindex(mBridgeName.c_str()); if (request.ifr_ifindex == 0) { return Result::error("Unable to get bridge %s interface index", mBridgeName.c_str()); } // Get bridge interface flags strlcpy(request.ifr_name, mBridgeName.c_str(), sizeof(request.ifr_name)); res = ::ioctl(mSocketFd, SIOCGIFFLAGS, &request); if (res != 0) { return Result::error("Unable to get interface flags for bridge %s: %s", mBridgeName.c_str(), strerror(errno)); } if ((request.ifr_flags & IFF_UP) != 0) { // Bridge is already up, it's ready to go return Result::success(); } // Bridge is not up, it needs to be up to work request.ifr_flags |= IFF_UP; res = ::ioctl(mSocketFd, SIOCSIFFLAGS, &request); if (res != 0) { return Result::error("Unable to set interface flags for bridge %s: %s", strerror(errno)); } return Result::success(); } Result Bridge::doInterfaceOperation(const std::string& interfaceName, unsigned long operation, const char* operationName) { struct ifreq request; memset(&request, 0, sizeof(request)); request.ifr_ifindex = if_nametoindex(interfaceName.c_str()); if (request.ifr_ifindex == 0) { return Result::error("Bridge unable to %s interface '%s', no such " "interface", operationName, interfaceName.c_str()); } strlcpy(request.ifr_name, mBridgeName.c_str(), sizeof(request.ifr_name)); int res = ::ioctl(mSocketFd, operation, &request); // An errno of EBUSY most likely indicates that the interface is already // part of the bridge. Ignore this. if (res < 0 && errno != EBUSY) { return Result::error("Bridge unable to %s interface '%s': %s", operationName, interfaceName.c_str(), strerror(errno)); } return Result::success(); }