fboss/agent/TunManager.h (100 lines of code) (raw):
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <folly/io/async/EventBase.h>
#include "fboss/agent/StateObserver.h"
#include "fboss/agent/state/Interface.h"
#include "fboss/agent/types.h"
#include <boost/container/flat_map.hpp>
extern "C" {
#include <netlink/object.h>
#include <netlink/socket.h>
}
namespace facebook::fboss {
class InterfaceMap;
class RxPacket;
class SwSwitch;
class TunIntf;
class TunManager : public StateObserver {
public:
TunManager(SwSwitch* sw, folly::EventBase* evb);
~TunManager() override;
/**
* Update the intfs_ map based on the given state update. This
* overrides the StateObserver stateUpdated api, which is always
* guaranteed to be called from the update thread.
*/
void stateUpdated(const StateDelta& delta) override;
/**
* Send a packet to host.
* This function can be called from any thread.
*
* @return true The packet is sent to host
* false The packet is dropped due to errors
*/
virtual bool sendPacketToHost(
InterfaceID dstIfID,
std::unique_ptr<RxPacket> pkt);
/**
* Performs probe procedure to read existing TUN interface info from the host
* and sync the new SwitchState
*
* This should really be only called externally once, after config is applied.
* After that all updates should come via the stateUpdated calls.
*
* SwSwitch calls this API when initial configuration is applied on agent
* restart.
*/
virtual void sync(std::shared_ptr<SwitchState> state);
void forceInitialSync();
/**
* This should be called externally only after initial sync has been
* performed.
*
* SwSwitch calls this API after calling initial sync when initial
* configuration is applied.
*/
virtual void startObservingUpdates();
/*
* Probe linux for tun interfaces already configured
*/
virtual void probe();
void stopProcessing();
private:
// no copy to assign
TunManager(const TunManager&) = delete;
TunManager& operator=(const TunManager&) = delete;
/**
* start/stop packet forwarding on all TUN interfaces
*/
void stop() const;
void start() const;
/**
* Add a TUN interface. It can happen two ways
* 1. During probe process when we discover existing Tun interface on linux
* 2. When we want to create a new TUN interface in linux
*/
void addExistingIntf(const std::string& name, int ifIndex);
void
addNewIntf(InterfaceID ifID, bool isUp, const Interface::Addresses& addrs);
// Remove an existing TUN interface
void removeIntf(InterfaceID ifID);
// A tun interface was changed, update the addresses accordingly
void updateIntf(InterfaceID ifID, const Interface::Addresses& addrs);
/**
* Bring UP/DOWN interfaces by mutating admin status in Linux
*/
void setIntfStatus(const std::string& ifName, int ifIndex, bool status);
/**
* Add/remove a route table.
*
* On Host we create a routing table for every switch interface which has
* v4/v6 default route with corresponding Tun interface as nexthop. Everything
* Forwarded to that routing table will comes out of Tun interface and it
* will eventually go out of corresponding switch interface.
*/
void addRemoveRouteTable(InterfaceID ifID, int ifIndex, bool add);
void addRouteTable(InterfaceID ifID, int ifIndex) {
addRemoveRouteTable(ifID, ifIndex, true);
}
void removeRouteTable(InterfaceID ifID, int ifIndex) {
addRemoveRouteTable(ifID, ifIndex, false);
}
/**
* Creates a tableId for given interface.
*/
int getTableId(InterfaceID ifID) const;
/**
* Add/remove an IP rule for source routing based on a given address
*
* The default route uses the management interface as the egress interface.
* Source routing is used to force the packets with source IP address matching
* one the front panel port IP addresses to be sent through the corresponding
* front panel ports instead of the management interface.
*/
void addRemoveSourceRouteRule(
InterfaceID ifID,
const folly::IPAddress& addr,
bool add);
/**
* Add/Remove an address to/from a TUN interface on the host
*/
void addRemoveTunAddress(
const std::string& ifName,
uint32_t ifIndex,
const folly::IPAddress& addr,
uint8_t mask,
bool add);
/**
* Add/Remove address as well source-routing-rule for TUN interface on host.
*/
void addTunAddress(
InterfaceID ifID,
const std::string& ifName,
uint32_t ifIndex,
folly::IPAddress addr,
uint8_t mask);
void removeTunAddress(
InterfaceID ifID,
const std::string& ifName,
uint32_t ifIndex,
folly::IPAddress addr,
uint8_t mask);
/**
* Netlink callback for processing and storing links
*/
static void linkProcessor(struct nl_object* obj, void* data);
/**
* Netlink callback for processing and storing addresses
*/
static void addressProcessor(struct nl_object* obj, void* data);
/**
* Lookup host for existing Tun interfaces and their addresses.
*/
virtual void doProbe(std::lock_guard<std::mutex>& mutex);
/**
* Add an address to a TUN interface during probe process.
*/
void addProbedAddr(int ifIndex, const folly::IPAddress& addr, uint8_t mask);
/**
* Get MTU of switch interface
*/
int getInterfaceMtu(InterfaceID ifID) const;
/**
* Get Interface statuses map from a given SwitchState. In switch each
* Interface/VLAN consists of multiple Ports. We derive state of Interface
* to be UP if alteast one of the port belonging to that interface is UP.
*/
static boost::container::flat_map<InterfaceID, bool> getInterfaceStatus(
std::shared_ptr<SwitchState> state);
template <
typename MAPNAME,
typename CHANGEFN,
typename ADDFN,
typename REMOVEFN>
void applyChanges(
const MAPNAME& oldMap,
const MAPNAME& newMap,
CHANGEFN changeFn,
ADDFN addFn,
REMOVEFN removeFn);
SwSwitch* sw_{nullptr};
folly::EventBase* evb_{nullptr};
// Netlink socket for managing interface/addresses in Host/Linux
nl_sock* sock_{nullptr};
/**
* The mutex used to protect `intfs_` which can be used by
* sync() could manipulate intfs_. Called on the thread that serves evb_.
* sendPacketToHost() uses intfs_, it can be called from any thread.
*/
boost::container::flat_map<InterfaceID, std::unique_ptr<TunIntf>> intfs_;
std::mutex mutex_;
// Whether the manager has registered itself to listen for state updates
// from sw_
bool observingState_{false};
// Initial probe done
bool probeDone_{false};
uint64_t numSyncs_{0};
enum : uint8_t {
/**
* The protocol value used to add the source routing IP rule and the
* default route in the routing table for source routing.
* Per rtnetlink.h, any value >= RTPROTO_STATIC(4) to identify us
*/
RTPROT_FBOSS = 80,
};
};
} // namespace facebook::fboss