1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2006-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation.  You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //--------------------------------------------------------------------------
19 
20 // host_attributes.cc  author davis mcpherson <davmcphe@cisco.com>
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "host_attributes.h"
27 
28 #include "hash/lru_cache_shared.h"
29 #include "main/shell.h"
30 #include "main/snort.h"
31 #include "main/snort_config.h"
32 #include "main/thread.h"
33 
34 using namespace snort;
35 
36 static const PegInfo host_attribute_pegs[] =
37 {
38     { CountType::MAX, "total_hosts", "maximum number of entries in the host attribute table" },
39     { CountType::SUM, "hosts_pruned", "number of LRU hosts pruned due to configured resource limits" },
40     { CountType::SUM, "dynamic_host_adds", "number of host additions after initial host file load" },
41     { CountType::SUM, "dynamic_service_adds", "number of service additions after initial host file load" },
42     { CountType::SUM, "dynamic_service_updates", "number of service updates after initial host file load" },
43     { CountType::SUM, "service_list_overflows", "number of service additions that failed due to configured resource limits" },
44     { CountType::END, nullptr, nullptr }
45 };
46 
47 template<typename Key, typename Value, typename Hash>
48 class HostLruSharedCache : public LruCacheShared<Key, Value, Hash>
49 {
50 public:
HostLruSharedCache(const size_t initial_size)51     HostLruSharedCache(const size_t initial_size) : LruCacheShared<Key, Value, Hash>(initial_size)
52     { }
53 };
54 
55 typedef HostLruSharedCache<snort::SfIp, HostAttributesDescriptor, HostAttributesCacheKey> HostAttributesSharedCache;
56 
57 class HostAttributesReloadTuner : public snort::ReloadResourceTuner
58 {
59 public:
60     HostAttributesReloadTuner() = default;
61 
tinit()62     bool tinit() override
63     {
64         HostAttributesManager::initialize();
65         return true;
66     }
67 
tune_packet_context()68     bool tune_packet_context() override
69     { return true; }
70 
tune_idle_context()71     bool tune_idle_context() override
72     { return true; }
73 };
74 
75 static THREAD_LOCAL HostAttributesSharedCache* active_cache = nullptr;
76 static HostAttributesSharedCache* swap_cache = nullptr;
77 static HostAttributesSharedCache* next_cache = nullptr;
78 static HostAttributesSharedCache* old_cache = nullptr;
79 static THREAD_LOCAL HostAttributeStats host_attribute_stats;
80 
update_service(uint16_t port,uint16_t protocol,SnortProtocolId snort_protocol_id,bool & updated,bool is_appid_service)81 bool HostAttributesDescriptor::update_service
82     (uint16_t port, uint16_t protocol, SnortProtocolId snort_protocol_id, bool& updated,
83     bool is_appid_service)
84 {
85     std::lock_guard<std::mutex> lck(host_attributes_lock);
86 
87     for ( auto& s : services)
88     {
89         if ( s.ipproto == protocol && (uint16_t)s.port == port )
90         {
91             if ( s.snort_protocol_id != snort_protocol_id )
92             {
93                 s.snort_protocol_id = snort_protocol_id;
94                 s.appid_service = is_appid_service;
95             }
96             updated = true;
97             return true;
98         }
99     }
100 
101     // service not found, add it
102     if ( services.size() < SnortConfig::get_conf()->get_max_services_per_host() )
103     {
104         updated = false;
105         services.emplace_back(HostServiceDescriptor(port, protocol, snort_protocol_id, is_appid_service));
106         return true;
107     }
108 
109     return false;
110 }
111 
clear_appid_services()112 void HostAttributesDescriptor::clear_appid_services()
113 {
114     std::lock_guard<std::mutex> lck(host_attributes_lock);
115     for ( auto s = services.begin(); s != services.end(); )
116     {
117         if ( s->appid_service and s->snort_protocol_id != UNKNOWN_PROTOCOL_ID )
118             s = services.erase(s);
119         else
120             s++;
121     }
122 }
123 
get_host_attributes(uint16_t port,HostAttriInfo * host_info) const124 void HostAttributesDescriptor::get_host_attributes(uint16_t port,HostAttriInfo* host_info) const
125 {
126     std::lock_guard<std::mutex> slk(host_attributes_lock);
127     host_info->frag_policy = policies.fragPolicy;
128     host_info->stream_policy = policies.streamPolicy;
129     host_info->snort_protocol_id = UNKNOWN_PROTOCOL_ID;
130     for ( auto& s : services )
131     {
132         if ( s.port == port )
133         {
134             host_info->snort_protocol_id = s.snort_protocol_id;
135             return;
136         }
137     }
138 }
load_hosts_file(snort::SnortConfig * sc,const char * fname)139 bool HostAttributesManager::load_hosts_file(snort::SnortConfig* sc, const char* fname)
140 {
141     delete next_cache;
142     next_cache = new HostAttributesSharedCache(sc->max_attribute_hosts);
143 
144     Shell sh(fname);
145     if ( sh.configure(sc, true) )
146     {
147         activate(sc);
148         return true;
149     }
150 
151     // loading of host file failed...
152     load_failure_cleanup();
153     return false;
154 }
155 
add_host(HostAttributesEntry host,snort::SnortConfig * sc)156 bool HostAttributesManager::add_host(HostAttributesEntry host, snort::SnortConfig* sc)
157 {
158     if ( !next_cache )
159         next_cache = new HostAttributesSharedCache(sc->max_attribute_hosts);
160 
161     return next_cache->find_else_insert(host->get_ip_addr(), host, true);
162 }
163 
activate(SnortConfig * sc)164 void HostAttributesManager::activate(SnortConfig* sc)
165 {
166     if ( next_cache == nullptr )
167         return;
168     old_cache = active_cache;
169     active_cache = next_cache;
170     swap_cache = next_cache;
171     next_cache = nullptr;
172 
173     if( active_cache != old_cache and Snort::is_reloading() )
174         sc->register_reload_resource_tuner(new HostAttributesReloadTuner);
175 }
176 
initialize()177 void HostAttributesManager::initialize()
178 { active_cache = swap_cache; }
179 
load_failure_cleanup()180 void HostAttributesManager::load_failure_cleanup()
181 {
182     delete next_cache;
183     next_cache = nullptr;
184 }
185 
swap_cleanup()186 void HostAttributesManager::swap_cleanup()
187 {
188     delete old_cache;
189     old_cache = nullptr;
190 }
191 
term()192 void HostAttributesManager::term()
193 { delete active_cache; }
194 
get_host_attributes(const snort::SfIp & host_ip,uint16_t port,HostAttriInfo * host_info)195 bool HostAttributesManager::get_host_attributes(const snort::SfIp& host_ip, uint16_t port, HostAttriInfo* host_info)
196 {
197     if ( !active_cache )
198         return false;
199 
200     HostAttributesEntry h = active_cache->find(host_ip);
201     if (h)
202     {
203         h->get_host_attributes(port, host_info);
204         return true;
205     }
206     return false;
207 }
208 
update_service(const snort::SfIp & host_ip,uint16_t port,uint16_t protocol,SnortProtocolId snort_protocol_id,bool is_appid_service)209 void HostAttributesManager::update_service(const snort::SfIp& host_ip, uint16_t port,
210     uint16_t protocol, SnortProtocolId snort_protocol_id, bool is_appid_service)
211 {
212     if ( active_cache )
213     {
214         bool created = false;
215         HostAttributesEntry host = active_cache->find_else_create(host_ip, &created);
216         if ( host )
217         {
218             if ( created )
219             {
220                 host_attribute_stats.dynamic_host_adds++;
221             }
222 
223             bool updated = false;
224             if ( host->update_service(port, protocol, snort_protocol_id, updated, is_appid_service) )
225             {
226                 if ( updated )
227                     host_attribute_stats.dynamic_service_updates++;
228                 else
229                     host_attribute_stats.dynamic_service_adds++;
230             }
231             else
232                 host_attribute_stats.service_list_overflows++;
233         }
234     }
235 }
236 
clear_appid_services()237 void HostAttributesManager::clear_appid_services()
238 {
239     if ( active_cache )
240     {
241         auto hosts = active_cache->get_all_data();
242         for ( auto& h : hosts )
243             h.second->clear_appid_services();
244     }
245 }
246 
get_num_host_entries()247 int32_t HostAttributesManager::get_num_host_entries()
248 {
249     if ( active_cache )
250         return active_cache->size();
251 
252     return -1;
253 }
254 
get_pegs()255 const PegInfo* HostAttributesManager::get_pegs()
256 { return (const PegInfo*)&host_attribute_pegs; }
257 
get_peg_counts()258 PegCount* HostAttributesManager::get_peg_counts()
259 {
260     if ( active_cache )
261     {
262         LruCacheSharedStats* cache_stats = (LruCacheSharedStats*) active_cache->get_counts();
263         host_attribute_stats.hosts_pruned = cache_stats->alloc_prunes;
264         host_attribute_stats.total_hosts = active_cache->size();
265     }
266 
267     return (PegCount*)&host_attribute_stats;
268 }
269 
270