blob: ba39eac3aa37244c5ca79f50423423e219b81450 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 <stdint.h>
#include <netinet/in.h>
#include <netlink/addr.h>
#include <netlink/cache.h>
#include <netlink/errno.h>
#include <netlink/netlink.h>
#include <netlink/object.h>
#include <netlink/socket.h>
#include <netlink/route/route.h>
#include <glog/logging.h>
#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/none.hpp>
#include <stout/stringify.hpp>
#include "linux/routing/internal.hpp"
#include "linux/routing/route.hpp"
#include "linux/routing/link/link.hpp"
using std::string;
using std::vector;
namespace routing {
namespace route {
Try<vector<Rule>> table()
{
Try<Netlink<struct nl_sock>> socket = routing::socket();
if (socket.isError()) {
return Error(socket.error());
}
// Dump all the routes (for IPv4) from kernel.
struct nl_cache* c = nullptr;
int error = rtnl_route_alloc_cache(socket.get().get(), AF_INET, 0, &c);
if (error != 0) {
return Error(nl_geterror(error));
}
Netlink<struct nl_cache> cache(c);
vector<Rule> results;
// Scan the routes and look for entries in the main routing table.
for (struct nl_object* o = nl_cache_get_first(cache.get());
o != nullptr; o = nl_cache_get_next(o)) {
struct rtnl_route* route = (struct rtnl_route*) o;
// TODO(jieyu): Currently, we assume each route in the routing
// table has only one hop (which is true in most environments).
if (rtnl_route_get_table(route) == RT_TABLE_MAIN &&
rtnl_route_get_nnexthops(route) == 1) {
CHECK_EQ(AF_INET, rtnl_route_get_family(route));
// Get the destination IP network if exists.
Option<net::IP::Network> destination;
struct nl_addr* dst = rtnl_route_get_dst(route);
if (dst != nullptr && nl_addr_get_len(dst) != 0) {
struct in_addr* addr = (struct in_addr*) nl_addr_get_binary_addr(dst);
Try<net::IP::Network> network = net::IP::Network::create(
net::IP(*addr),
nl_addr_get_prefixlen(dst));
if (network.isError()) {
return Error(
"Invalid IP network format from the routing table: " +
network.error());
}
destination = network.get();
}
// Get the default gateway if exists.
Option<net::IP> gateway;
struct rtnl_nexthop* hop = rtnl_route_nexthop_n(route, 0);
struct nl_addr* gw = rtnl_route_nh_get_gateway(CHECK_NOTNULL(hop));
if (gw != nullptr && nl_addr_get_len(gw) != 0) {
struct in_addr* addr = (struct in_addr*) nl_addr_get_binary_addr(gw);
gateway = net::IP(*addr);
}
// Get the destination link.
int index = rtnl_route_nh_get_ifindex(hop);
Result<string> link = link::name(index);
if (link.isError()) {
return Error("Failed to get the link name: " + link.error());
} else if (link.isNone()) {
return Error("Link of index " + stringify(index) + " is not found");
}
results.push_back(Rule(destination, gateway, link.get()));
}
}
return results;
}
Result<net::IP> defaultGateway()
{
Try<vector<Rule>> rules = table();
if (rules.isError()) {
return Error("Failed to get the routing table: " + rules.error());
}
foreach (const Rule& rule, rules.get()) {
if (rule.destination.isNone() && rule.gateway.isSome()) {
return rule.gateway.get();
}
}
return None();
}
} // namespace route {
} // namespace routing {