]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #3740: appid: Support for IPv4 and IPv6 subnets for First Packet API
authorSreeja Athirkandathil Narayanan (sathirka) <sathirka@cisco.com>
Thu, 2 Feb 2023 21:48:58 +0000 (21:48 +0000)
committerSreeja Athirkandathil Narayanan (sathirka) <sathirka@cisco.com>
Thu, 2 Feb 2023 21:48:58 +0000 (21:48 +0000)
Merge in SNORT/snort3 from ~OSTEPANO/snort3:subnet_first_packet_api to master

Squashed commit of the following:

commit f6bcb8fbe09223f566cafc3a40c3e57c174998e0
Author: Umang Sharma <umasharm@cisco.com>
Date:   Fri Dec 9 06:38:37 2022 -0500

    appid: Support for IPv4 and IPv6 subnets for First Packet API

src/network_inspectors/appid/appid_config.h
src/network_inspectors/appid/host_port_app_cache.cc
src/network_inspectors/appid/host_port_app_cache.h
src/network_inspectors/appid/lua_detector_api.cc

index ddd8539a4141e2bbc341310b359e53c316c69e2a..d8bd715e9fef58816191906aa44935acc3c474b2 100644 (file)
@@ -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)
index 94b1a43bdb741cc746ab144a819986be85d59492..90ddbdaf7232f73dcbb2a2f5d43debf01eebf942 100644 (file)
 
 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<uint32_t*>(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<uint32_t*>(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<HostPortKey, HostAppIdsVal>::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<HostPortKey, HostAppIdsVal>::iterator check_cache;
+        check_cache = cache_first_ip.find(hk);
+        if (check_cache != cache_first_ip.end())
+            return &check_cache->second;
+    }
+
+    for (std::map<FirstPktkey ,HostAppIdsVal>::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;
 }
 
index 608e97dcd22154fd06008ad945d65f52b9b49cdc..642edc51965b6336420b1310c4b6807531b3525a 100644 (file)
@@ -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<HostPortKey, HostPortVal> cache;
-    std::map<HostPortKey, HostAppIdsVal> cache_first;
+    std::map<HostPortKey, HostAppIdsVal> cache_first_ip;
+    std::multimap<FirstPktkey, HostAppIdsVal> cache_first_subnet;
 };
 
 #endif
index 678b5c031e88ed7f55790432fe0ff841ea828a3c..b7e9d34c384827f76cab96a5a8043d8b0476b7d7 100644 (file)
@@ -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<string> 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;