blob: db56b7d10549f4907150213b2aeede397bfc1a02 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <patchpanel/proto_bindings/patchpanel_service.pb.h>
#include "patchpanel/datapath.h"
#include "patchpanel/routing_service.h"
namespace patchpanel {
// This class manages the iptables rules for traffic counters, and queries
// iptables to get the counters when a request comes. This class will set up
// several iptable rules to track the counters for each possible combination of
// {bytes, packets} x (Traffic source) x (shill Device) x {rx, tx} x {IPv4,
// IPv6}. These counters will never be removed after they are set up, and thus
// they represent the traffic usage from boot time.
// Implementation details
// Rules: All the rules/chains for accounting are in (INPUT, FORWARD or
// POSTROUTING) chain in the mangle table. These rules take effect after routing
// and will not change the fate of a packet. When a new interface comes up, we
// will create the following new rules/chains (using both iptables and
// ip6tables):
// - Two accounting chains:
// - For rx packets, `rx_{ifname}` for INPUT and FORWARD chains;
// - For tx packets, `tx_{ifname}` for POSTROUTING chain.
// - One accounting rule in each accounting chain for every source defined in
// RoutingService plus one final accounting rule for untagged traffic.
// - Jumping rules for each accounting chain in the corresponding prebuilt
// chain, which matches packets with this new interface.
// The above accounting rules and chains will never be removed once created, so
// we will check if one rule exists before creating it. Jumping rules are added
// and removed dynamically based on shill physical Device and shill vpn Device
// creation and removal events.
// Query: Two commands (iptables and ip6tables) will be executed in the mangle
// table to get all the chains and rules. And then we perform a text parsing on
// the output to get the counters.
class CountersService {
struct CounterKey {
std::string ifname;
TrafficCounter::Source source;
TrafficCounter::IpFamily ip_family;
bool operator<(const CounterKey& rhs) const;
struct Counter {
uint64_t rx_bytes = 0;
uint64_t rx_packets = 0;
uint64_t tx_bytes = 0;
uint64_t tx_packets = 0;
CountersService(Datapath* datapath);
~CountersService() = default;
// Adds accounting rules and jump rules for a new physical device if this is
// the first time this device is seen.
void OnPhysicalDeviceAdded(const std::string& ifname);
// Removes jump rules for a physical device.
void OnPhysicalDeviceRemoved(const std::string& ifname);
// Adds accounting rules and jump rules for a new VPN device.
void OnVpnDeviceAdded(const std::string& ifname);
// Removes jump rules for a VPN device.
void OnVpnDeviceRemoved(const std::string& ifname);
// Collects and returns counters from all the existing iptables rules.
// |devices| is the set of interfaces for which counters should be returned,
// any unknown interfaces will be ignored. If |devices| is empty, counters for
// all known interfaces will be returned. An empty map will be returned on
// any failure.
std::map<CounterKey, Counter> GetCounters(
const std::set<std::string>& devices);
// Creates an iptables chain in the mangle table. Returns true if the chain
// was created, or false if the chain already existed.
bool MakeAccountingChain(const std::string& chain_name);
bool AddAccountingRule(const std::string& chain_name, TrafficSource source);
// Installs the required accounting chains and rules for the target
// |chain_tag| if they did not exist already.
void SetupAccountingRules(const std::string& chain_tag);
// Installs jump rules in POSTROUTING to count traffic ingressing and
// egressing |ifname| with the accounting target |chain_tag|.
void SetupJumpRules(const std::string& op,
const std::string& ifname,
const std::string& chain_tag);
Datapath* datapath_;
TrafficCounter::Source TrafficSourceToProto(TrafficSource source);
TrafficSource ProtoToTrafficSource(TrafficCounter::Source source);
} // namespace patchpanel