Jie Jiang | 31a0b4e | 2020-07-09 15:06:16 +0900 | [diff] [blame] | 1 | // 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 | #include "patchpanel/counters_service.h" |
| 6 | |
| 7 | #include <set> |
| 8 | #include <string> |
| 9 | #include <vector> |
| 10 | |
| 11 | namespace patchpanel { |
| 12 | |
| 13 | namespace { |
| 14 | |
| 15 | constexpr char kMangleTable[] = "mangle"; |
| 16 | |
| 17 | } // namespace |
| 18 | |
| 19 | CountersService::CountersService(ShillClient* shill_client, |
| 20 | MinijailedProcessRunner* runner) |
| 21 | : shill_client_(shill_client), runner_(runner) { |
| 22 | // Triggers the callback manually to make sure no device is missed. |
| 23 | OnDeviceChanged(shill_client_->get_devices(), {}); |
| 24 | shill_client_->RegisterDevicesChangedHandler(base::BindRepeating( |
| 25 | &CountersService::OnDeviceChanged, weak_factory_.GetWeakPtr())); |
| 26 | } |
| 27 | |
| 28 | void CountersService::OnDeviceChanged(const std::set<std::string>& added, |
Jie Jiang | cf74915 | 2020-07-09 22:20:58 +0900 | [diff] [blame] | 29 | const std::set<std::string>& removed) { |
| 30 | for (const auto& ifname : added) |
| 31 | SetupChainsAndRules(ifname); |
| 32 | } |
Jie Jiang | 31a0b4e | 2020-07-09 15:06:16 +0900 | [diff] [blame] | 33 | |
| 34 | void CountersService::IptablesNewChain(const std::string& chain_name) { |
| 35 | // There is no straightforward way to check if a chain exists or not. |
| 36 | runner_->iptables(kMangleTable, {"-N", chain_name, "-w"}, |
| 37 | false /*log_failures*/); |
| 38 | runner_->ip6tables(kMangleTable, {"-N", chain_name, "-w"}, |
| 39 | false /*log_failures*/); |
| 40 | } |
| 41 | |
| 42 | void CountersService::IptablesNewRule(std::vector<std::string> params) { |
| 43 | DCHECK_GT(params.size(), 0); |
| 44 | const std::string action = params[0]; |
| 45 | DCHECK(action == "-I" || action == "-A"); |
| 46 | params.emplace_back("-w"); |
| 47 | |
| 48 | params[0] = "-C"; |
| 49 | if (runner_->iptables(kMangleTable, params, false /*log_failures*/) != 0) { |
| 50 | params[0] = action; |
| 51 | runner_->iptables(kMangleTable, params); |
| 52 | } |
| 53 | |
| 54 | params[0] = "-C"; |
| 55 | if (runner_->ip6tables(kMangleTable, params, false /*log_failures*/) != 0) { |
| 56 | params[0] = action; |
| 57 | runner_->ip6tables(kMangleTable, params); |
| 58 | } |
| 59 | } |
| 60 | |
Jie Jiang | cf74915 | 2020-07-09 22:20:58 +0900 | [diff] [blame] | 61 | void CountersService::SetupChainsAndRules(const std::string& ifname) { |
| 62 | // For each group, we need to create 1) an accounting chain, 2) a jumping rule |
| 63 | // matching |ifname|, and 3) accounting rule(s) in the chain. |
| 64 | // Note that the length of a chain name must less than 29 chars and IFNAMSIZ |
| 65 | // is 16 so we can only use at most 12 chars for the prefix. |
| 66 | |
| 67 | // Egress traffic in FORWARD chain. Only traffic for interface-type sources |
| 68 | // will be counted by these rules. |
| 69 | const std::string egress_forward_chain = "tx_fwd_" + ifname; |
| 70 | IptablesNewChain(egress_forward_chain); |
| 71 | IptablesNewRule({"-A", "FORWARD", "-o", ifname, "-j", egress_forward_chain}); |
| 72 | SetupAccountingRules(egress_forward_chain); |
| 73 | |
| 74 | // Egress traffic in POSTROUTING chain. Only traffic for host-type sources |
| 75 | // will be counted by these rules, by having a "-m owner --socket-exists" in |
| 76 | // the jumping rule. Traffic via "FORWARD -> POSTROUTING" does not have a |
| 77 | // socket so will only be counted in FORWARD, while traffic from OUTPUT will |
| 78 | // always have an associated socket. |
| 79 | const std::string egress_postrouting_chain = "tx_postrt_" + ifname; |
| 80 | IptablesNewChain(egress_postrouting_chain); |
| 81 | IptablesNewRule({"-A", "POSTROUTING", "-o", ifname, "-m", "owner", |
| 82 | "--socket-exists", "-j", egress_postrouting_chain}); |
| 83 | SetupAccountingRules(egress_postrouting_chain); |
| 84 | |
| 85 | // Ingress traffic in FORWARD chain. Only traffic for interface-type sources |
| 86 | // will be counted by these rules. |
| 87 | const std::string ingress_forward_chain = "rx_fwd_" + ifname; |
| 88 | IptablesNewChain(ingress_forward_chain); |
| 89 | IptablesNewRule({"-A", "FORWARD", "-i", ifname, "-j", ingress_forward_chain}); |
| 90 | SetupAccountingRules(ingress_forward_chain); |
| 91 | |
| 92 | // Ingress traffic in INPUT chain. Only traffic for host-type sources will be |
| 93 | // counted by these rules. |
| 94 | const std::string ingress_input_chain = "rx_input_" + ifname; |
| 95 | IptablesNewChain(ingress_input_chain); |
| 96 | IptablesNewRule({"-A", "INPUT", "-i", ifname, "-j", ingress_input_chain}); |
| 97 | SetupAccountingRules(ingress_input_chain); |
| 98 | } |
| 99 | |
| 100 | void CountersService::SetupAccountingRules(const std::string& chain_name) { |
| 101 | // TODO(jiejiang): This function will be extended to matching on fwmark for |
| 102 | // different sources. |
| 103 | IptablesNewRule({"-A", chain_name}); |
| 104 | } |
| 105 | |
Jie Jiang | 31a0b4e | 2020-07-09 15:06:16 +0900 | [diff] [blame] | 106 | } // namespace patchpanel |