136 lines
4.3 KiB
136 lines
4.3 KiB
/*
|
|
* 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();
|
|
}
|