nfs: add a portmap client

Summary:
The portmap protocol allows for service discovery and registration against the
per-host rpcbind daemon. An NFS server will need to register against it to be
mountable.

The portmap_util binary is here for testing purposes and will not be used in
EdenFS.

This code was written by wez.

Reviewed By: kmancini

Differential Revision: D25986694

fbshipit-source-id: 1eee7238fdf70c8c4937e685da91ad08d46befe4
This commit is contained in:
Xavier Deguillard 2021-02-01 09:26:33 -08:00 committed by Facebook GitHub Bot
parent 7c7f50ef9d
commit 1b0d345774
3 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/nfs/portmap/PortmapClient.h"
#include <common/network/NetworkUtil.h>
#include <folly/Exception.h>
#include <folly/SocketAddress.h>
#include <folly/String.h>
#include <folly/logging/xlog.h>
using folly::IOBuf;
using folly::IOBufQueue;
using folly::SocketAddress;
namespace facebook::eden {
namespace {
constexpr uint32_t kPortmapPortNumber = 111;
constexpr uint32_t kPortmapProgNumber = 100000;
/*
* Ideally we should use version 3 and 4, as that appears to have better
* support for ipv6. For now, and since the goal is to only support a loopback
* NFS, let's use version 2 and ipv4.
*/
constexpr uint32_t kPortmapVersionNumber = 2;
constexpr uint32_t kPortmapSet = 1;
constexpr uint32_t kPortmapUnSet = 2;
constexpr uint32_t kPortmapGetPort = 3;
} // namespace
EDEN_XDR_SERDE_IMPL(PortmapMapping, prog, vers, prot, port);
PortmapClient::PortmapClient()
: client_(SocketAddress(
network::NetworkUtil::getLocalIPv4(),
kPortmapPortNumber)) {
#ifdef __APPLE__
{
// Connect to the portmap "tickler" socket.
// This causes launchd to spawn `rpcbind` and bring up the portmap service.
auto addr = SocketAddress::makeFromPath("/var/run/portmap.socket");
sockaddr_storage stg;
auto len = addr.getAddress(&stg);
tickler_ = folly::netops::socket(addr.getFamily(), SOCK_STREAM, 0);
folly::checkUnixError(
folly::netops::connect(tickler_, (sockaddr*)&stg, len),
"connect to ",
addr.getPath());
}
#endif
client_.connect();
}
bool PortmapClient::unsetMapping(PortmapMapping map) {
return client_.call<bool, PortmapMapping>(
kPortmapProgNumber, kPortmapVersionNumber, kPortmapUnSet, map);
}
bool PortmapClient::setMapping(PortmapMapping map) {
return client_.call<bool, PortmapMapping>(
kPortmapProgNumber, kPortmapVersionNumber, kPortmapSet, map);
}
uint32_t PortmapClient::getPort(PortmapMapping map) {
return client_.call<uint32_t, PortmapMapping>(
kPortmapProgNumber, kPortmapVersionNumber, kPortmapGetPort, map);
}
} // namespace facebook::eden

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <folly/net/NetworkSocket.h>
#include "eden/fs/nfs/rpc/Rpc.h"
#include "eden/fs/nfs/rpc/StreamClient.h"
namespace facebook::eden {
struct PortmapMapping {
uint32_t prog;
uint32_t vers;
uint32_t prot;
uint32_t port;
static constexpr uint32_t kProtoTcp = 6;
static constexpr uint32_t kProtoUdp = 17;
bool operator==(const PortmapMapping&) const;
};
EDEN_XDR_SERDE_DECL(PortmapMapping);
class PortmapClient {
public:
bool setMapping(PortmapMapping map);
bool unsetMapping(PortmapMapping map);
uint32_t getPort(PortmapMapping map);
PortmapClient();
private:
#ifdef __APPLE__
folly::NetworkSocket tickler_;
#endif
StreamClient client_;
};
} // namespace facebook::eden

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include <folly/SocketAddress.h>
#include <folly/init/Init.h>
#include <folly/logging/Init.h>
#include <folly/logging/xlog.h>
#include "eden/fs/nfs/portmap/PortmapClient.h"
using namespace facebook::eden;
FOLLY_INIT_LOGGING_CONFIG("eden=INFO");
int main(int argc, char** argv) {
folly::init(&argc, &argv);
PortmapClient client;
auto port =
client.getPort(PortmapMapping{100003, 3, PortmapMapping::kProtoTcp, 0});
XLOG(INFO) << "Got port: " << port;
// Try to set a bogus port for NFS.
// This will fail if there is already an NFS daemon running
XLOG(INFO) << "Set mapping: "
<< client.setMapping(
PortmapMapping{100003, 3, PortmapMapping::kProtoTcp, 123});
// Read back the current port
auto newPort =
client.getPort(PortmapMapping{100003, 3, PortmapMapping::kProtoTcp, 0});
XLOG(INFO) << "Got new port: " << newPort;
return 0;
}