|
| 1 | +/** |
| 2 | + * Copyright (c) 2014-present, Facebook, Inc. |
| 3 | + * |
| 4 | + * This source code is licensed under the MIT license found in the |
| 5 | + * LICENSE file in the root directory of this source tree. |
| 6 | + */ |
| 7 | +#include <map> |
| 8 | +#include <memory> |
| 9 | +#include <string> |
| 10 | +#include <thread> |
| 11 | + |
| 12 | +#include <benchmark/benchmark.h> |
| 13 | +#include <fbzmq/async/ZmqEventLoop.h> |
| 14 | +#include <fbzmq/zmq/Zmq.h> |
| 15 | +#include <folly/Exception.h> |
| 16 | +#include <folly/Format.h> |
| 17 | +#include <folly/MacAddress.h> |
| 18 | +#include <folly/Random.h> |
| 19 | +#include <folly/gen/Base.h> |
| 20 | +#include <folly/init/Init.h> |
| 21 | +#include <folly/test/TestUtils.h> |
| 22 | +#include <openr/fib/tests/PrefixGenerator.h> |
| 23 | +#include <openr/nl/NetlinkSocket.h> |
| 24 | +#include <openr/platform/NetlinkFibHandler.h> |
| 25 | + |
| 26 | +extern "C" { |
| 27 | +#include <net/if.h> |
| 28 | +#include <netlink/route/link/veth.h> |
| 29 | +#include <netlink/route/route.h> |
| 30 | +#include <sys/ioctl.h> |
| 31 | +} |
| 32 | + |
| 33 | +using namespace openr::fbnl; |
| 34 | + |
| 35 | +namespace { |
| 36 | +// Virtual interfaces |
| 37 | +const std::string kVethNameX("vethTestX"); |
| 38 | +const std::string kVethNameY("vethTestY"); |
| 39 | +// Prefix length of a subnet |
| 40 | +static const uint8_t kBitMaskLen = 128; |
| 41 | +// Number of nexthops |
| 42 | +const uint8_t kNumOfNexthops = 128; |
| 43 | + |
| 44 | +} // namespace |
| 45 | + |
| 46 | +namespace openr { |
| 47 | + |
| 48 | +const int16_t kFibId{static_cast<int16_t>(thrift::FibClient::OPENR)}; |
| 49 | + |
| 50 | +// This class creates virtual interface (veths) |
| 51 | +// which the Benchmark test can use to add routes (via interface) |
| 52 | +class NetlinkFibWrapper { |
| 53 | + public: |
| 54 | + struct RouteCallbackContext { |
| 55 | + struct nl_cache* routeCache{nullptr}; |
| 56 | + std::vector<Route> results; |
| 57 | + }; |
| 58 | + |
| 59 | + struct AddressCallbackContext { |
| 60 | + struct nl_cache* linkeCache{nullptr}; |
| 61 | + std::vector<IfAddress> results; |
| 62 | + }; |
| 63 | + |
| 64 | + NetlinkFibWrapper() { |
| 65 | + // Allocate socket |
| 66 | + socket_ = nl_socket_alloc(); |
| 67 | + nl_connect(socket_, NETLINK_ROUTE); |
| 68 | + rtnl_link_alloc_cache(socket_, AF_UNSPEC, &linkCache_); |
| 69 | + rtnl_addr_alloc_cache(socket_, &addrCache_); |
| 70 | + rtnl_route_alloc_cache(socket_, AF_UNSPEC, 0, &routeCache_); |
| 71 | + |
| 72 | + // Virtual interface and virtual link |
| 73 | + link_ = rtnl_link_veth_alloc(); |
| 74 | + auto peerLink = rtnl_link_veth_get_peer(link_); |
| 75 | + rtnl_link_set_name(link_, kVethNameX.c_str()); |
| 76 | + rtnl_link_set_name(peerLink, kVethNameY.c_str()); |
| 77 | + nl_object_put(OBJ_CAST(peerLink)); |
| 78 | + |
| 79 | + rtnl_link_add(socket_, link_, NLM_F_CREATE); |
| 80 | + |
| 81 | + nl_cache_refill(socket_, linkCache_); |
| 82 | + addAddress(kVethNameX, "169.254.0.101"); |
| 83 | + addAddress(kVethNameY, "169.254.0.102"); |
| 84 | + |
| 85 | + // set interface status to up |
| 86 | + bringUpIntf(kVethNameX); |
| 87 | + bringUpIntf(kVethNameY); |
| 88 | + |
| 89 | + // Create NetlinkProtocolSocket |
| 90 | + std::unique_ptr<openr::Netlink::NetlinkProtocolSocket> nlProtocolSocket; |
| 91 | + nlProtocolSocket = |
| 92 | + std::make_unique<openr::Netlink::NetlinkProtocolSocket>(&evl2); |
| 93 | + nlProtocolSocketThread = std::thread([&]() { |
| 94 | + nlProtocolSocket->init(); |
| 95 | + evl2.run(); |
| 96 | + evl2.waitUntilStopped(); |
| 97 | + }); |
| 98 | + evl2.waitUntilRunning(); |
| 99 | + |
| 100 | + // Create netlink route socket |
| 101 | + nlSocket = std::make_shared<NetlinkSocket>( |
| 102 | + &evl, nullptr, true, std::move(nlProtocolSocket)); |
| 103 | + |
| 104 | + // Run the zmq event loop in its own thread |
| 105 | + // We will either timeout if expected events are not received |
| 106 | + // or stop after we receive expected events |
| 107 | + eventThread = std::thread([&]() { |
| 108 | + evl.run(); |
| 109 | + evl.waitUntilStopped(); |
| 110 | + }); |
| 111 | + evl.waitUntilRunning(); |
| 112 | + |
| 113 | + // Start FibService thread |
| 114 | + fibHandler = std::make_shared<NetlinkFibHandler>(&evl, nlSocket); |
| 115 | + } |
| 116 | + |
| 117 | + ~NetlinkFibWrapper() { |
| 118 | + if (evl.isRunning()) { |
| 119 | + evl.stop(); |
| 120 | + eventThread.join(); |
| 121 | + } |
| 122 | + if (evl2.isRunning()) { |
| 123 | + evl2.stop(); |
| 124 | + nlProtocolSocketThread.join(); |
| 125 | + } |
| 126 | + |
| 127 | + nlSocket.reset(); |
| 128 | + |
| 129 | + rtnl_link_delete(socket_, link_); |
| 130 | + nl_cache_free(linkCache_); |
| 131 | + nl_cache_free(addrCache_); |
| 132 | + nl_cache_free(routeCache_); |
| 133 | + nl_socket_free(socket_); |
| 134 | + rtnl_link_veth_release(link_); |
| 135 | + } |
| 136 | + |
| 137 | + fbzmq::Context context; |
| 138 | + std::shared_ptr<NetlinkSocket> nlSocket; |
| 139 | + fbzmq::ZmqEventLoop evl; |
| 140 | + fbzmq::ZmqEventLoop evl2; |
| 141 | + std::thread eventThread; |
| 142 | + std::thread nlProtocolSocketThread; |
| 143 | + std::shared_ptr<NetlinkFibHandler> fibHandler; |
| 144 | + PrefixGenerator prefixGenerator; |
| 145 | + |
| 146 | + struct rtnl_link* link_{nullptr}; |
| 147 | + struct nl_sock* socket_{nullptr}; |
| 148 | + struct nl_cache* linkCache_{nullptr}; |
| 149 | + struct nl_cache* addrCache_{nullptr}; |
| 150 | + struct nl_cache* routeCache_{nullptr}; |
| 151 | + |
| 152 | + private: |
| 153 | + void |
| 154 | + addAddress(const std::string& ifName, const std::string& address) { |
| 155 | + int ifIndex = rtnl_link_name2i(linkCache_, ifName.c_str()); |
| 156 | + |
| 157 | + auto addrMask = std::make_pair(folly::IPAddress(address), 16); |
| 158 | + struct nl_addr* nlAddr = nl_addr_build( |
| 159 | + addrMask.first.family(), |
| 160 | + (void*)addrMask.first.bytes(), |
| 161 | + addrMask.first.byteCount()); |
| 162 | + nl_addr_set_prefixlen(nlAddr, addrMask.second); |
| 163 | + |
| 164 | + struct rtnl_addr* addr = rtnl_addr_alloc(); |
| 165 | + rtnl_addr_set_local(addr, nlAddr); |
| 166 | + rtnl_addr_set_ifindex(addr, ifIndex); |
| 167 | + rtnl_addr_add(socket_, addr, 0); |
| 168 | + nl_addr_put(nlAddr); |
| 169 | + rtnl_addr_put(addr); |
| 170 | + } |
| 171 | + |
| 172 | + static void |
| 173 | + bringUpIntf(const std::string& ifName) { |
| 174 | + // Prepare socket |
| 175 | + auto sockFd = socket(PF_INET, SOCK_DGRAM, 0); |
| 176 | + |
| 177 | + // Prepare request |
| 178 | + struct ifreq ifr; |
| 179 | + memset(&ifr, 0, sizeof(ifr)); |
| 180 | + folly::strlcpy(ifr.ifr_name, ifName.c_str(), IFNAMSIZ); |
| 181 | + |
| 182 | + // Get existing flags |
| 183 | + ioctl(sockFd, SIOCGIFFLAGS, static_cast<void*>(&ifr)); |
| 184 | + |
| 185 | + // Mutate flags and set them back |
| 186 | + ifr.ifr_flags |= IFF_UP; |
| 187 | + ioctl(sockFd, SIOCSIFFLAGS, static_cast<void*>(&ifr)); |
| 188 | + } |
| 189 | +}; |
| 190 | + |
| 191 | +static void |
| 192 | +getProcessTimeBM(benchmark::State& state) { |
| 193 | + /* Benchmark test to measure the time performance */ |
| 194 | + auto netlinkFibWrapper = std::make_unique<NetlinkFibWrapper>(); |
| 195 | + const uint32_t numOfPrefixes = state.range(0); |
| 196 | + |
| 197 | + // Randomly generate IPV6 prefixes |
| 198 | + auto prefixes = netlinkFibWrapper->prefixGenerator.ipv6PrefixGenerator( |
| 199 | + numOfPrefixes, kBitMaskLen); |
| 200 | + |
| 201 | + for (auto _ : state) { |
| 202 | + auto routes = std::make_unique<std::vector<thrift::UnicastRoute>>(); |
| 203 | + routes->reserve(prefixes.size()); |
| 204 | + |
| 205 | + // Update routes by randomly regenerating nextHops for kDeltaSize prefixes. |
| 206 | + for (auto index = 0; index < numOfPrefixes; index++) { |
| 207 | + routes->emplace_back(createUnicastRoute( |
| 208 | + prefixes[index], |
| 209 | + netlinkFibWrapper->prefixGenerator.getRandomNextHopsUnicast( |
| 210 | + kNumOfNexthops, kVethNameY))); |
| 211 | + } |
| 212 | + |
| 213 | + // Add new routes through netlink |
| 214 | + netlinkFibWrapper->fibHandler |
| 215 | + ->future_addUnicastRoutes(kFibId, std::move(routes)) |
| 216 | + .wait(); |
| 217 | + } |
| 218 | + |
| 219 | + state.SetItemsProcessed(state.iterations()); |
| 220 | +} |
| 221 | +BENCHMARK(getProcessTimeBM)->RangeMultiplier(10)->Range(10, 10000); |
| 222 | +} // namespace openr |
| 223 | + |
| 224 | +int |
| 225 | +main(int argc, char** argv) { |
| 226 | + /* Use main instead of macro BENCHMARK_MAIN() since we need folly::init*/ |
| 227 | + folly::init(&argc, &argv); |
| 228 | + ::benchmark::Initialize(&argc, argv); |
| 229 | + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) |
| 230 | + return 1; |
| 231 | + ::benchmark::RunSpecifiedBenchmarks(); |
| 232 | +} |
0 commit comments