| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/base/no_destructor.h" |
| #include "absl/base/thread_annotations.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/log/log.h" |
| #include "absl/synchronization/mutex.h" |
| #include "software_metrics_config.pb.h" |
| #include "resource.pb.h" |
| #include "software_metrics.pb.h" |
| #include "tlbmc/utils/command_executor.h" |
| #include "tlbmc/utils/shell_command_executor.h" |
| |
| namespace milotic_tlbmc { |
| |
| /* |
| The ss linux command is 'socketstats'. |
| -t displays only TCP sockets |
| -a displays all sockets (listening and non-listening) |
| -n displays numeric host names |
| This is piped into awk which does the following: |
| 1. Skip the first line since it's just headers and we don't care about them |
| For each row after that: |
| 2. Target column number 4 as part of the split command and cuts it into two |
| using the colon as a delimiter (Looks like ${IP}:${PORT}) |
| 3. Prints TCP socket state column (column 1 or $1 in the script) as well as the |
| last portion of the column 4 split in step 2 which happens to be the port. |
| */ |
| // clang-format off |
| /* |
| ss -tan output will be: |
| |
| State Recv-Q Send-Q Local Address:Port Peer Address:Port |
| LISTEN 0 10 *:23 *:* |
| LISTEN 0 4096 *:22 *:* |
| TIME-WAIT 0 0 [2002:a05:613a:84d1:fd01::]:22 [2001:4860:f803:3fe::b]:47104 |
| ESTAB 0 0 [2002:a05:613a:84d1:fd01::]:443 [2002:a05:613a:84d1:1000::]:48022 |
| */ |
| // clang-format on |
| /* |
| after being piped into the awk script becomes: |
| LISTEN 23 |
| LISTEN 22 |
| TIME-WAIT 22 |
| ESTAB 443 |
| */ |
| |
| static const absl::NoDestructor<std::string> kSocketStatCmd( |
| "ss -tan | awk 'NR > 1 { split($4, a, \":\"); print $1, a[length(a)] }'"); |
| |
| /* |
| The nft command is used to interact with the netfilter subsystem |
| list chain inet filter output displays all output chain rules |
| -j ensures the output is in JSON format |
| The JSON is then processed by the software_metrics object. |
| |
| Example: |
| { |
| "nftables": [ |
| { |
| "metainfo": { |
| "version": "1.0.9", |
| "release_name": "Old Doc Yak #3", |
| "json_schema_version": 1 |
| } |
| }, |
| { |
| "table": { |
| "family": "inet", |
| "name": "filter", |
| "handle": 25 |
| } |
| }, |
| { |
| "rule": { |
| "family": "inet", |
| "table": "filter", |
| "chain": "count_port_22", |
| "handle": 18, |
| "comment": "tcp-server-22-synack", |
| "expr": [ |
| { |
| "counter": { |
| "packets": 41, |
| "bytes": 3200 |
| } |
| } |
| ] |
| } |
| } |
| ] |
| } |
| */ |
| |
| static const absl::NoDestructor<std::string> kNetFilterCmd( |
| "nft -j list table inet filter"); |
| |
| // Any implementation of this interface must be thread-safe regarding the listed |
| // functions. |
| class SoftwareMetrics { |
| public: |
| static absl::StatusOr<std::unique_ptr<SoftwareMetrics>> Create( |
| const SoftwareMetricsConfig& config); |
| |
| static absl::StatusOr<std::unique_ptr<SoftwareMetrics>> |
| CreateWithExecutorForUnitTest( |
| const SoftwareMetricsConfig& config, |
| absl::flat_hash_map<std::string, absl::StatusOr<std::string>>&); |
| |
| static SoftwareMetricsAttributesStatic CreateStaticAttributes( |
| const SoftwareMetricsConfig& software_metrics_config); |
| |
| SoftwareMetricsValue GetSoftwareMetricsValues() const |
| ABSL_LOCKS_EXCLUDED(metric_data_mutex_) { |
| absl::MutexLock lock(&metric_data_mutex_); |
| return values_; |
| } |
| |
| virtual ~SoftwareMetrics() = default; |
| |
| SocketStatStates GetSocketStatStates() const |
| ABSL_LOCKS_EXCLUDED(metric_data_mutex_) { |
| absl::MutexLock lock(&metric_data_mutex_); |
| return values_.socket_stat_state(); |
| } |
| |
| NetFilterStates GetNetFilterStates() const |
| ABSL_LOCKS_EXCLUDED(metric_data_mutex_) { |
| absl::MutexLock lock(&metric_data_mutex_); |
| return values_.netfilter_state(); |
| } |
| |
| SoftwareMetricsAttributesDynamic GetSoftwareMetricsAttributesDynamic() const { |
| absl::MutexLock lock(&metric_attributes_dynamic_mutex_); |
| return metrics_attributes_dynamic_; |
| } |
| |
| const SoftwareMetricsAttributesStatic& GetSoftwareMetricsAttributesStatic() |
| const { |
| return metrics_attributes_static_; |
| } |
| |
| // Refreshes the metric data once asynchronously. The metric data will be |
| // updated when the refresh is done. |
| // The `callback` will be called when the refresh is done. The callback is |
| // supposed to be blocking. |
| void RefreshOnce(absl::AnyInvocable<void()> callback) |
| ABSL_LOCKS_EXCLUDED(metric_data_mutex_); |
| |
| CommandExecutor* GetExecutorForTest() const { |
| return executor_.get(); |
| } |
| |
| private: |
| explicit SoftwareMetrics( |
| const SoftwareMetricsAttributesStatic& software_attributes_static) |
| : metrics_attributes_static_(software_attributes_static), |
| ss_port_set_(metrics_attributes_static_.software_metrics_config() |
| .socket_stat_ports() |
| .begin(), |
| metrics_attributes_static_.software_metrics_config() |
| .socket_stat_ports() |
| .end()), |
| nf_port_set_(metrics_attributes_static_.software_metrics_config() |
| .netfilter_ports() |
| .begin(), |
| metrics_attributes_static_.software_metrics_config() |
| .netfilter_ports() |
| .end()), |
| executor_(std::make_unique<ShellExecutor>()) {} |
| |
| SoftwareMetrics(SoftwareMetricsAttributesStatic& software_attributes_static, |
| std::unique_ptr<CommandExecutor> executor) |
| : metrics_attributes_static_(software_attributes_static), |
| ss_port_set_(metrics_attributes_static_.software_metrics_config() |
| .socket_stat_ports() |
| .begin(), |
| metrics_attributes_static_.software_metrics_config() |
| .socket_stat_ports() |
| .end()), |
| nf_port_set_(metrics_attributes_static_.software_metrics_config() |
| .netfilter_ports() |
| .begin(), |
| metrics_attributes_static_.software_metrics_config() |
| .netfilter_ports() |
| .end()), |
| executor_(std::move(executor)) {} |
| void StoreData(SoftwareMetricsValue* metric_data) |
| ABSL_LOCKS_EXCLUDED(metric_data_mutex_); |
| |
| absl::StatusOr<NetFilterStates> UpdateNetFilterStats(); |
| absl::StatusOr<SocketStatStates> UpdateSocketStats(); |
| |
| const SoftwareMetricsAttributesStatic metrics_attributes_static_; |
| const absl::flat_hash_set<int32_t> ss_port_set_; |
| const absl::flat_hash_set<int32_t> nf_port_set_; |
| |
| mutable absl::Mutex metric_attributes_dynamic_mutex_; |
| SoftwareMetricsAttributesDynamic metrics_attributes_dynamic_ |
| ABSL_GUARDED_BY(metric_attributes_dynamic_mutex_); |
| |
| std::unique_ptr<CommandExecutor> executor_; |
| mutable absl::Mutex metric_data_mutex_; |
| SoftwareMetricsValue values_ ABSL_GUARDED_BY(metric_data_mutex_); |
| }; |
| |
| } // namespace milotic_tlbmc |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_ |