blob: adbe21e6195c3e1785ea4935fa7203b9ed2bd3a7 [file] [log] [blame]
/*
* 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 <errno.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
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();
}