From: Sreeja Athirkandathil Narayanan (sathirka) Date: Thu, 2 Feb 2023 21:48:58 +0000 (+0000) Subject: Pull request #3740: appid: Support for IPv4 and IPv6 subnets for First Packet API X-Git-Tag: 3.1.55.0~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=56fd30451142c63748ac996957a55594ef30958c;p=thirdparty%2Fsnort3.git Pull request #3740: appid: Support for IPv4 and IPv6 subnets for First Packet API Merge in SNORT/snort3 from ~OSTEPANO/snort3:subnet_first_packet_api to master Squashed commit of the following: commit f6bcb8fbe09223f566cafc3a40c3e57c174998e0 Author: Umang Sharma Date: Fri Dec 9 06:38:37 2022 -0500 appid: Support for IPv4 and IPv6 subnets for First Packet API --- diff --git a/src/network_inspectors/appid/appid_config.h b/src/network_inspectors/appid/appid_config.h index ddd8539a4..d8bd715e9 100644 --- a/src/network_inspectors/appid/appid_config.h +++ b/src/network_inspectors/appid/appid_config.h @@ -169,10 +169,10 @@ public: return host_port_cache.add(sc, ip, port, proto, type, appid); } - bool host_first_pkt_add(const snort::SnortConfig* sc, const snort::SfIp* ip, uint16_t port, + bool host_first_pkt_add(const snort::SnortConfig* sc, const snort::SfIp* ip, uint32_t* netmask, uint16_t port, IpProtocol proto, AppId protocol_appid, AppId client_appid, AppId web_appid, unsigned reinspect) { - return first_pkt_cache.add_host(sc, ip, port, proto, protocol_appid, client_appid, web_appid, reinspect); + return first_pkt_cache.add_host(sc, ip, netmask, port, proto, protocol_appid, client_appid, web_appid, reinspect); } HostAppIdsVal* host_first_pkt_find(const snort::SfIp* ip, uint16_t port, IpProtocol proto) diff --git a/src/network_inspectors/appid/host_port_app_cache.cc b/src/network_inspectors/appid/host_port_app_cache.cc index 94b1a43bd..90ddbdaf7 100644 --- a/src/network_inspectors/appid/host_port_app_cache.cc +++ b/src/network_inspectors/appid/host_port_app_cache.cc @@ -34,6 +34,73 @@ using namespace snort; +inline void apply_min_ip_range(SfIp& ip, const uint32_t* netmask) +{ + if (ip.get_family() == AF_INET) + { + uint32_t tmp_val = ip.get_ip4_value() & netmask[3]; + ip.set(&tmp_val, AF_INET); + } + else if (ip.get_family() == AF_INET6) + { + uint32_t* tmp_val = const_cast(ip.get_ip6_ptr()); + tmp_val[0] = tmp_val[0] & netmask[0]; + tmp_val[1] = tmp_val[1] & netmask[1]; + tmp_val[2] = tmp_val[2] & netmask[2]; + tmp_val[3] = tmp_val[3] & netmask[3]; + ip.set(tmp_val, AF_INET6); + } +} + +inline void apply_max_ip_range(SfIp& ip, const uint32_t* netmask) +{ + if (ip.get_family() == AF_INET) + { + uint32_t tmp_val = ip.get_ip4_value() | ~netmask[3]; + ip.set(&tmp_val, AF_INET); + } + else if (ip.get_family() == AF_INET6) + { + uint32_t* tmp_val = const_cast(ip.get_ip6_ptr()); + + tmp_val[0] = tmp_val[0] | ~netmask[0]; + tmp_val[1] = tmp_val[1] | ~netmask[1]; + tmp_val[2] = tmp_val[2] | ~netmask[2]; + tmp_val[3] = tmp_val[3] | ~netmask[3]; + + ip.set(tmp_val, AF_INET6); + } +} + +inline bool check_ip_range(const SfIp& max, const SfIp& min, const SfIp& ip, const uint32_t* netmask) +{ + if (max.get_family() != ip.get_family()) + return false; + + if (max.is_ip4()) + { + SfIp tmp = ip; + apply_min_ip_range(tmp, netmask); + if (tmp.get_ip4_value() == min.get_ip4_value()) + { + apply_max_ip_range(tmp, netmask); + return tmp.get_ip4_value() == max.get_ip4_value(); + } + } + else if (max.is_ip6()) + { + SfIp tmp = ip; + apply_min_ip_range(tmp, netmask); + if (memcmp(tmp.get_ip6_ptr(), min.get_ip6_ptr(), 16) == 0) + { + apply_max_ip_range(tmp, netmask); + return memcmp(tmp.get_ip6_ptr(), max.get_ip6_ptr(), 16) == 0; + } + } + + return false; +} + HostPortVal* HostPortCache::find(const SfIp* ip, uint16_t port, IpProtocol protocol, const OdpContext& odp_ctxt) { @@ -73,45 +140,86 @@ bool HostPortCache::add(const SnortConfig* sc, const SfIp* ip, uint16_t port, Ip return true; } - HostAppIdsVal* HostPortCache::find_on_first_pkt(const SfIp* ip, uint16_t port, IpProtocol protocol, const OdpContext& odp_ctxt) { - HostPortKey hk; + uint16_t lookup_port = (odp_ctxt.allow_port_wildcard_host_cache)? 0 : port; - hk.ip = *ip; - hk.port = (odp_ctxt.allow_port_wildcard_host_cache)? 0 : port; - hk.proto = protocol; + if (!cache_first_ip.empty()) + { + HostPortKey hk; - std::map::iterator it; - it = cache_first.find(hk); - if (it != cache_first.end()) - return &it->second; - else - return nullptr; + hk.ip = *ip; + hk.port = lookup_port; + hk.proto = protocol; + + std::map::iterator check_cache; + check_cache = cache_first_ip.find(hk); + if (check_cache != cache_first_ip.end()) + return &check_cache->second; + } + + for (std::map::iterator iter = cache_first_subnet.begin(); iter != cache_first_subnet.end(); ++iter) + { + if (iter->first.port == lookup_port and iter->first.proto == protocol and + check_ip_range(iter->first.max_network_range, iter->first.network_address, *ip, &iter->first.netmask[0])) + { + return &iter->second; + } + } + + return nullptr; } -bool HostPortCache::add_host(const SnortConfig* sc, const SfIp* ip, uint16_t port, IpProtocol proto, +bool HostPortCache::add_host(const SnortConfig* sc, const SfIp* ip, uint32_t* netmask, uint16_t port, IpProtocol proto, AppId protocol_appId, AppId client_appId, AppId web_appId, unsigned reinspect) { - HostPortKey hk; - HostAppIdsVal hv; - - hk.ip = *ip; - AppIdInspector* inspector = - (AppIdInspector*)InspectorManager::get_inspector(MOD_NAME, false, sc); - assert(inspector); - const AppIdContext& ctxt = inspector->get_ctxt(); - hk.port = (ctxt.get_odp_ctxt().allow_port_wildcard_host_cache)? 0 : port; - hk.proto = proto; - - hv.protocol_appId = protocol_appId; - hv.client_appId = client_appId; - hv.web_appId = web_appId; - hv.reinspect = reinspect; - - cache_first[ hk ] = hv; - + if (!netmask) + { + HostPortKey hk; + HostAppIdsVal hv; + + hk.ip = *ip; + AppIdInspector* inspector = + (AppIdInspector*)InspectorManager::get_inspector(MOD_NAME, false, sc); + assert(inspector); + const AppIdContext& ctxt = inspector->get_ctxt(); + hk.port = (ctxt.get_odp_ctxt().allow_port_wildcard_host_cache)? 0 : port; + hk.proto = proto; + + hv.protocol_appId = protocol_appId; + hv.client_appId = client_appId; + hv.web_appId = web_appId; + hv.reinspect = reinspect; + + cache_first_ip[ hk ] = hv; + } + else + { + FirstPktkey hk; + HostAppIdsVal hv; + + hk.network_address = *ip; + apply_min_ip_range(hk.network_address, netmask); + hk.max_network_range = hk.network_address; + apply_max_ip_range(hk.max_network_range, netmask); + + memcpy(&hk.netmask[0], netmask, 16); + + AppIdInspector* inspector = + (AppIdInspector*)InspectorManager::get_inspector(MOD_NAME, false, sc); + assert(inspector); + const AppIdContext& ctxt = inspector->get_ctxt(); + hk.port = (ctxt.get_odp_ctxt().allow_port_wildcard_host_cache)? 0 : port; + hk.proto = proto; + + hv.protocol_appId = protocol_appId; + hv.client_appId = client_appId; + hv.web_appId = web_appId; + hv.reinspect = reinspect; + + cache_first_subnet.emplace(hk, hv); + } return true; } diff --git a/src/network_inspectors/appid/host_port_app_cache.h b/src/network_inspectors/appid/host_port_app_cache.h index 608e97dcd..642edc519 100644 --- a/src/network_inspectors/appid/host_port_app_cache.h +++ b/src/network_inspectors/appid/host_port_app_cache.h @@ -59,6 +59,37 @@ struct HostPortKey }; PADDING_GUARD_END +PADDING_GUARD_BEGIN +struct FirstPktkey +{ + FirstPktkey() + { + max_network_range.clear(); + network_address.clear(); + port = 0; + proto = IpProtocol::PROTO_NOT_SET; + } + + bool operator<(const FirstPktkey& right) const + { + if ((htonl(right.netmask[0]) < htonl(this->netmask[0])) or + (htonl(right.netmask[1]) < htonl(this->netmask[1])) or + (htonl(right.netmask[2]) < htonl(this->netmask[2])) or + (htonl(right.netmask[3]) < htonl(this->netmask[3]))) + return true; + else + return false; + } + + uint32_t netmask[4]; + snort::SfIp max_network_range; + snort::SfIp network_address; + uint16_t port; + IpProtocol proto; + char padding; +}; +PADDING_GUARD_END + struct HostPortVal { AppId appId; @@ -81,19 +112,22 @@ public: unsigned type, AppId); HostAppIdsVal* find_on_first_pkt(const snort::SfIp*, uint16_t port, IpProtocol, const OdpContext&); - bool add_host(const snort::SnortConfig*, const snort::SfIp*, uint16_t port, IpProtocol, + bool add_host(const snort::SnortConfig*, const snort::SfIp*, uint32_t* netmask, uint16_t port, IpProtocol, AppId, AppId, AppId, unsigned reinspect); void dump(); ~HostPortCache() { cache.clear(); - cache_first.clear(); + cache_first_ip.clear(); + cache_first_subnet.clear(); } private: + std::map cache; - std::map cache_first; + std::map cache_first_ip; + std::multimap cache_first_subnet; }; #endif diff --git a/src/network_inspectors/appid/lua_detector_api.cc b/src/network_inspectors/appid/lua_detector_api.cc index 678b5c031..b7e9d34c3 100644 --- a/src/network_inspectors/appid/lua_detector_api.cc +++ b/src/network_inspectors/appid/lua_detector_api.cc @@ -1237,14 +1237,90 @@ static int detector_add_host_first_pkt_application(lua_State* L) uint32_t client_appid = lua_tointeger(L, ++index); uint32_t web_appid = lua_tointeger(L, ++index); unsigned reinspect = lua_tointeger(L, ++index); - - /* Extract IP, Port, protocol */ + + /* Extract Network IP and netmask */ size_t ipaddr_size = 0; - const char* ip_str = lua_tolstring(L, ++index, &ipaddr_size); - if (!ip_str or !ipaddr_size or !convert_string_to_address(ip_str, &ip_address)) + uint32_t netmask32[4] = { 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu }; + bool netmask_parsed = false; + const char* cidr_str = lua_tolstring(L, ++index, &ipaddr_size); + vector tokens; + + if (!cidr_str or !ipaddr_size) { - ErrorMessage("%s: Invalid IP address: %s\n",__func__, ip_str); + ErrorMessage("%s: No IP address provided\n", "First packet API"); return 0; + } + + if (strchr(cidr_str, '/') == nullptr) + { + if (!convert_string_to_address(cidr_str, &ip_address)) + { + ErrorMessage("%s: Invalid IP address: %s\n", "First packet API", cidr_str); + return 0; + } + } + else + { + stringstream ss(cidr_str); + string temp_str; + + while (getline(ss, temp_str, '/')) + { + tokens.push_back(temp_str); + } + + const char* netip_str = tokens[0].c_str(); + + if (!netip_str or !convert_string_to_address(netip_str, &ip_address)) + { + ErrorMessage("%s: Invalid IP address: %s\n", "First packet API", netip_str); + return 0; + } + + if (all_of(tokens[1].begin(), tokens[1].end(), ::isdigit)) + { + int bits = stoi(tokens[1].c_str()); + if (strchr(netip_str, '.')) + { + if (bits < 0 or bits > 32) + { + ErrorMessage("%s: Invalid IPv4 prefix range: %d\n","First packet API", bits); + return 0; + } + } + else if (strchr(netip_str, ':')) + { + if (bits < 0 or bits > 128) { + ErrorMessage("%s: Invalid IPv6 prefix range: %d\n","First packet API", bits); + return 0; + } + } + + if (bits < 32 and !strchr(netip_str, ':')) + netmask32[3] = bits > 0 ? (0xFFFFFFFFu << (32 - bits)) : 0xFFFFFFFFu; + else + { + for (int i = 3; i >= 0; --i) + { + auto tmp_bits = 32 + (32 * i) - bits; + + if (tmp_bits > 0) + netmask32[i] = tmp_bits >= 32 ? 0 : (0xFFFFFFFFu << tmp_bits); + } + } + + for (int i = 0; i < 4; i++) + { + netmask32[i] = (uint32_t)htonl(netmask32[i]); + } + + netmask_parsed = true; + } + else + { + ErrorMessage("%s: Invalid prefix bit: %s\n", "First packet API", tokens[1].c_str()); + return 0; + } } unsigned port = lua_tointeger(L, ++index); @@ -1257,7 +1333,7 @@ static int detector_add_host_first_pkt_application(lua_State* L) lua_pop(L, 1); if (!ud->get_odp_ctxt().host_first_pkt_add( - sc, &ip_address, (uint16_t)port, proto, protocol_appid, client_appid, web_appid, reinspect)) + sc, &ip_address, netmask_parsed ? netmask32 : nullptr, (uint16_t)port, proto, protocol_appid, client_appid, web_appid, reinspect)) ErrorMessage("%s:Failed to backend call first pkt add\n",__func__); return 0;