blob: b2e8cdec9b330b20dde21cd78c5705bef5c7822e [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
Jie Jiang31a0b4e2020-07-09 15:06:16 +090016#include "patchpanel/minijailed_process_runner.h"
17#include "patchpanel/shill_client.h"
18
19namespace patchpanel {
20
21// This class manages the iptables rules for traffic counters, and queries
22// iptables to get the counters when a request comes. This class will set up
23// several iptable rules to track the counters for each possible combination of
24// {bytes, packets} x (Traffic source) x (shill device) x {rx, tx} x {IPv4,
25// IPv6}. These counters will never be removed after they are set up, and thus
26// they represent the traffic usage from boot time.
27//
Jie Jiang31a0b4e2020-07-09 15:06:16 +090028// Implementation details
29//
30// Rules: All the rules/chains for accounting are in (INPUT, FORWARD or
31// POSTROUTING) chain in the mangle table. These rules take effect after routing
32// and will not change the fate of a packet. When a new interface comes up, we
33// will create the following new rules/chains (using both iptables and
34// ip6tables):
35// - Four accounting chains:
36// - For rx packets, `ingress_input_{ifname}` and `ingress_forward_{ifname}`
37// for INPUT and FORWARD chain, respectively;
38// - For tx packets, `egress_postrouting_{ifname}` and
39// `egress_forward_{ifname}` for POSTROUTING and FORWARD chain,
40// respectively. Note that we use `--socket-exists` in POSTROUTING chain to
41// avoid packets from FORWARD being matched again here.
42// - One accounting rule in each accounting chain, which provides the actual
43// counter for accounting. We will extend this to several rules when source
44// marking is ready.
45// - One jumping rule for each accounting chain in the corresponding prebuilt
46// chain, which matches packets with this new interface.
47// The above rules and chains will never be removed once created, so we will
48// check if one rule exists before creating it.
49//
50// Query: Two commands (iptables and ip6tables) will be executed in the mangle
51// table to get all the chains and rules. And then we perform a text parsing on
52// the output to get the counters. Counters for the same entry will be merged
53// before return.
54class CountersService {
55 public:
Jie Jianged0b1cc2020-07-10 15:55:33 +090056 using SourceDevice = std::pair<TrafficCounter::Source, std::string>;
57 struct Counter {
58 Counter() = default;
59 Counter(uint64_t rx_bytes,
60 uint64_t rx_packets,
61 uint64_t tx_bytes,
62 uint64_t tx_packets);
63
64 uint64_t rx_bytes = 0;
65 uint64_t rx_packets = 0;
66 uint64_t tx_bytes = 0;
67 uint64_t tx_packets = 0;
68 };
69
Jie Jiang31a0b4e2020-07-09 15:06:16 +090070 CountersService(ShillClient* shill_client, MinijailedProcessRunner* runner);
71 ~CountersService() = default;
72
Jie Jianged0b1cc2020-07-10 15:55:33 +090073 // Collects and returns counters from all the existing iptables rules.
74 // |devices| is the set of interfaces for which counters should be returned,
75 // any unknown interfaces will be ignored. If |devices| is empty, counters for
76 // all known interfaces will be returned. An empty map will be returned on
77 // any failure. Note that currently all traffic to/from an interface will be
78 // counted by (UNKNOWN, ifname), i.e., no other sources except for UNKNOWN are
79 // used.
80 std::map<SourceDevice, Counter> GetCounters(
81 const std::set<std::string>& devices);
82
Jie Jiang31a0b4e2020-07-09 15:06:16 +090083 private:
84 // TODO(b/161060333): Move the following two functions elsewhere.
85 // Creates a new chain using both iptables and ip6tables in the mangle table.
86 void IptablesNewChain(const std::string& chain_name);
87
88 // Creates a new rule using both iptables and ip6tables in the mangle table.
89 // The first element in |params| should be "-I" (insert) or "-A" (append), and
90 // this function will replace it with "-C" to do the check before executing
91 // the actual insert or append command. This function will also append "-w" to
92 // |params|. Note that |params| is passed by value because it will be modified
93 // inside the function, and the normal pattern to use this function is passing
94 // an rvalue (e.g., `IptablesNewRule({"-I", "INPUT", ...})`), so no extra copy
95 // should happen in such cases.
96 void IptablesNewRule(std::vector<std::string> params);
97
Jie Jiangcf749152020-07-09 22:20:58 +090098 // Installs the required chains and rules for the given shill device.
99 void SetupChainsAndRules(const std::string& ifname);
100 // Installs the accounting rules in the given accounting chain. Currently we
101 // only need one rule to match all the traffic, without distinguishing
102 // different sources.
103 void SetupAccountingRules(const std::string& chain_name);
104
Jie Jiang31a0b4e2020-07-09 15:06:16 +0900105 void OnDeviceChanged(const std::set<std::string>& added,
106 const std::set<std::string>& removed);
107
108 ShillClient* shill_client_;
109 MinijailedProcessRunner* runner_;
110
111 base::WeakPtrFactory<CountersService> weak_factory_{this};
112};
113
114} // namespace patchpanel
115
116#endif // PATCHPANEL_COUNTERS_SERVICE_H_