blob: 6fba471d23699f57d2634ed918cfdaf487675fa0 [file] [log] [blame]
Jie Jiang31a0b4e2020-07-09 15:06:16 +09001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef PATCHPANEL_COUNTERS_SERVICE_H_
6#define PATCHPANEL_COUNTERS_SERVICE_H_
7
Jie Jianged0b1cc2020-07-10 15:55:33 +09008#include <map>
Jie Jiang31a0b4e2020-07-09 15:06:16 +09009#include <set>
10#include <string>
Jie Jianged0b1cc2020-07-10 15:55:33 +090011#include <utility>
Jie Jiang31a0b4e2020-07-09 15:06:16 +090012#include <vector>
13
Jie Jianged0b1cc2020-07-10 15:55:33 +090014#include <patchpanel/proto_bindings/patchpanel_service.pb.h>
15
Hugo Benichicd27f4e2020-11-19 18:32:23 +090016#include "patchpanel/datapath.h"
Jie Jiang31a0b4e2020-07-09 15:06:16 +090017#include "patchpanel/minijailed_process_runner.h"
18#include "patchpanel/shill_client.h"
19
20namespace patchpanel {
21
22// This class manages the iptables rules for traffic counters, and queries
23// iptables to get the counters when a request comes. This class will set up
24// several iptable rules to track the counters for each possible combination of
25// {bytes, packets} x (Traffic source) x (shill device) x {rx, tx} x {IPv4,
26// IPv6}. These counters will never be removed after they are set up, and thus
27// they represent the traffic usage from boot time.
28//
Jie Jiang31a0b4e2020-07-09 15:06:16 +090029// Implementation details
30//
31// Rules: All the rules/chains for accounting are in (INPUT, FORWARD or
32// POSTROUTING) chain in the mangle table. These rules take effect after routing
33// and will not change the fate of a packet. When a new interface comes up, we
34// will create the following new rules/chains (using both iptables and
35// ip6tables):
36// - Four accounting chains:
37// - For rx packets, `ingress_input_{ifname}` and `ingress_forward_{ifname}`
38// for INPUT and FORWARD chain, respectively;
39// - For tx packets, `egress_postrouting_{ifname}` and
40// `egress_forward_{ifname}` for POSTROUTING and FORWARD chain,
41// respectively. Note that we use `--socket-exists` in POSTROUTING chain to
42// avoid packets from FORWARD being matched again here.
43// - One accounting rule in each accounting chain, which provides the actual
44// counter for accounting. We will extend this to several rules when source
45// marking is ready.
46// - One jumping rule for each accounting chain in the corresponding prebuilt
47// chain, which matches packets with this new interface.
48// The above rules and chains will never be removed once created, so we will
49// check if one rule exists before creating it.
50//
51// Query: Two commands (iptables and ip6tables) will be executed in the mangle
52// table to get all the chains and rules. And then we perform a text parsing on
53// the output to get the counters. Counters for the same entry will be merged
54// before return.
55class CountersService {
56 public:
Jie Jianged0b1cc2020-07-10 15:55:33 +090057 using SourceDevice = std::pair<TrafficCounter::Source, std::string>;
58 struct Counter {
59 Counter() = default;
60 Counter(uint64_t rx_bytes,
61 uint64_t rx_packets,
62 uint64_t tx_bytes,
63 uint64_t tx_packets);
64
65 uint64_t rx_bytes = 0;
66 uint64_t rx_packets = 0;
67 uint64_t tx_bytes = 0;
68 uint64_t tx_packets = 0;
69 };
70
Hugo Benichicd27f4e2020-11-19 18:32:23 +090071 CountersService(ShillClient* shill_client,
72 Datapath* datapath,
73 MinijailedProcessRunner* runner);
Jie Jiang31a0b4e2020-07-09 15:06:16 +090074 ~CountersService() = default;
75
Jie Jianged0b1cc2020-07-10 15:55:33 +090076 // Collects and returns counters from all the existing iptables rules.
77 // |devices| is the set of interfaces for which counters should be returned,
78 // any unknown interfaces will be ignored. If |devices| is empty, counters for
79 // all known interfaces will be returned. An empty map will be returned on
80 // any failure. Note that currently all traffic to/from an interface will be
81 // counted by (UNKNOWN, ifname), i.e., no other sources except for UNKNOWN are
82 // used.
83 std::map<SourceDevice, Counter> GetCounters(
84 const std::set<std::string>& devices);
85
Jie Jiang31a0b4e2020-07-09 15:06:16 +090086 private:
Hugo Benichicd27f4e2020-11-19 18:32:23 +090087 bool MakeAccountingChain(const std::string& chain_name);
Jie Jiang31a0b4e2020-07-09 15:06:16 +090088
89 // Creates a new rule using both iptables and ip6tables in the mangle table.
90 // The first element in |params| should be "-I" (insert) or "-A" (append), and
91 // this function will replace it with "-C" to do the check before executing
92 // the actual insert or append command. This function will also append "-w" to
93 // |params|. Note that |params| is passed by value because it will be modified
94 // inside the function, and the normal pattern to use this function is passing
95 // an rvalue (e.g., `IptablesNewRule({"-I", "INPUT", ...})`), so no extra copy
96 // should happen in such cases.
97 void IptablesNewRule(std::vector<std::string> params);
98
Jie Jiangcf749152020-07-09 22:20:58 +090099 // Installs the required chains and rules for the given shill device.
100 void SetupChainsAndRules(const std::string& ifname);
101 // Installs the accounting rules in the given accounting chain. Currently we
102 // only need one rule to match all the traffic, without distinguishing
103 // different sources.
104 void SetupAccountingRules(const std::string& chain_name);
105
Jie Jiang31a0b4e2020-07-09 15:06:16 +0900106 void OnDeviceChanged(const std::set<std::string>& added,
107 const std::set<std::string>& removed);
108
109 ShillClient* shill_client_;
Hugo Benichicd27f4e2020-11-19 18:32:23 +0900110 Datapath* datapath_;
Jie Jiang31a0b4e2020-07-09 15:06:16 +0900111 MinijailedProcessRunner* runner_;
112
113 base::WeakPtrFactory<CountersService> weak_factory_{this};
114};
115
116} // namespace patchpanel
117
118#endif // PATCHPANEL_COUNTERS_SERVICE_H_