Skip to content

Commit 7ca8649

Browse files
committed
Merge branch 'tg-36-ip-prefix-clf' into 'main'
IP Prefix Classifier See merge request feta/wif-group/libwif!40
2 parents 78614ed + 2089cb5 commit 7ca8649

7 files changed

Lines changed: 423 additions & 3 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @file
3+
* @author Richard Plny <plnyrich@fit.cvut.cz>
4+
* @brief IP prefix classifier interface
5+
*
6+
* SPDX-License-Identifier: BSD-3-Clause
7+
*/
8+
9+
#pragma once
10+
11+
#include "wif/classifiers/classifier.hpp"
12+
#include "wif/utils/ipPrefix.hpp"
13+
14+
#include <algorithm>
15+
#include <memory>
16+
#include <vector>
17+
18+
namespace WIF {
19+
20+
/**
21+
* @brief Classifier performing blocklist detection
22+
*/
23+
class IpPrefixClassifier : public Classifier {
24+
public:
25+
/**
26+
* @brief Construct an IP Prefix Classifier object with empty blocklist
27+
*/
28+
IpPrefixClassifier() = default;
29+
30+
/**
31+
* @brief Construct a new IP Prefix Classifier object
32+
* @param blocklist std::vector of blocklisted IP prefixes
33+
*/
34+
IpPrefixClassifier(const std::vector<IpPrefix>& blocklist);
35+
36+
/**
37+
* @brief Getter for current IP prefix blocklist
38+
* @return const std::vector<IpPrefix>& current blocklist
39+
*/
40+
const std::vector<IpPrefix>& getBlocklist() const noexcept { return m_blocklist; }
41+
42+
/**
43+
* @brief Update blocklist
44+
* Old blocklist is destructed and replaced by new one
45+
* @param blocklist std::vector of new IP prefixes on blocklist
46+
*/
47+
void updateBlocklist(const std::vector<IpPrefix>& blocklist);
48+
49+
/**
50+
* @brief Classify single flowFeature object
51+
* ClfResult contains non-zero double value if flowFeatures contained a field with IP from
52+
* blocklist, otherwise the double value is set to zero
53+
*
54+
* @param flowFeatures flow features to classify
55+
* @return ClfResult result of the classification
56+
*/
57+
ClfResult classify(const FlowFeatures& flowFeatures) override;
58+
59+
/**
60+
* @brief Classify a burst of flow features
61+
*
62+
* @param burstOfFlowsFeatures the burst of flow features to classify
63+
* @return std::vector<ClfResult> std::vector<ClfResult> the results of the classification
64+
*/
65+
std::vector<ClfResult> classify(const std::vector<FlowFeatures>& burstOfFlowsFeatures) override;
66+
67+
private:
68+
bool findIpAddress(const IpAddress& ipAddress);
69+
70+
std::vector<IpPrefix> m_blocklist;
71+
};
72+
73+
} // namespace WIF

include/wif/storage/ipAddress.hpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class IpAddress {
7070
*/
7171
bool empty() const noexcept;
7272

73+
/**
74+
* Getter for version of the IP address.
75+
* @return IpVersion version of the held IP address
76+
*/
77+
IpVersion getVersion() const noexcept;
78+
7379
/**
7480
* @brief Checks if the IP address is version 4
7581
* @return bool
@@ -136,13 +142,22 @@ class IpAddress {
136142
*/
137143
friend IpAddress operator&(const IpAddress& l, const IpAddress& r);
138144

145+
/**
146+
* @brief Bitwise NOT operator
147+
*
148+
* @param address IP address whose bits will be flipped
149+
* @return IpAddress IP address with flipped bits
150+
*/
151+
friend IpAddress operator~(const IpAddress& address);
152+
139153
/**
140154
* @brief Comparison operator <
141155
*
142-
* @param other IP address object to print
143-
* @return bool
156+
* @param l left operand
157+
* @param r right operand
158+
* @return bool true if l is less than r
144159
*/
145-
bool operator<(const IpAddress& other) const;
160+
friend bool operator<(const IpAddress& l, const IpAddress& r);
146161

147162
private:
148163
constexpr static size_t SIZE_IN_UINT8 = 16;
@@ -159,6 +174,9 @@ class IpAddress {
159174

160175
constexpr static uint32_t IPV4_FILLING_CONSTANT = 0xFFFFFFFF;
161176

177+
bool compareV4(const IpAddress& other) const;
178+
bool compareV6(const IpAddress& other) const;
179+
162180
void createV4FromBytes(const uint8_t* bytes, bool isLittleEndian);
163181
void createV6FromBytes(const uint8_t* bytes, bool isLittleEndian);
164182
void createV4FromBytesLittleEndian(const uint8_t* bytes);

include/wif/utils/ipPrefix.hpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* @file
3+
* @author Richard Plny <plnyrich@fit.cvut.cz>
4+
* @brief IP prefix interface
5+
* Based on:
6+
* https://github.com/CESNET/nemea-modules-ng/blob/main/modules/whitelist/src/ipAddressPrefix.hpp
7+
*
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#pragma once
12+
13+
#include "wif/storage/ipAddress.hpp"
14+
15+
namespace WIF {
16+
17+
/**
18+
* @brief Class representing an IP prefix (or an IP subnet)
19+
*/
20+
class IpPrefix {
21+
public:
22+
/**
23+
* @brief Max bit lenth of IPv4 prefix
24+
*/
25+
static constexpr size_t IPV4_MAX_PREFIX_LENGTH = 32;
26+
27+
/**
28+
* @brief Max bit lenth of IPv6 prefix
29+
*/
30+
static constexpr size_t IPV6_MAX_PREFIX_LENGTH = 128;
31+
32+
/**
33+
* @brief Construct a new IP Prefix object representing just 1 IP address
34+
*
35+
* @param address single IP address
36+
*/
37+
IpPrefix(const IpAddress& address);
38+
39+
/**
40+
* @brief Construct a new IP Prefix object
41+
* @param addressStr string representation of IP prefix
42+
* @param prefixLength number of bits of the prefix
43+
*/
44+
IpPrefix(const std::string& addressStr, size_t prefixLength);
45+
46+
/**
47+
* @brief Construct a new IP Prefix object
48+
* @param address IP prefix
49+
* @param prefixLength number of bits of the prefix
50+
*/
51+
IpPrefix(const IpAddress& address, size_t prefixLength);
52+
53+
/**
54+
* @brief Getter for the number of bits used by prefix
55+
* @return size_t number of bits of the prefix
56+
*/
57+
size_t prefixLength() const noexcept { return m_prefixLength; }
58+
59+
/**
60+
* @brief Getter for number of IP addresses in the prefix
61+
* @return size_t number of IP addresses in the prefix
62+
*/
63+
size_t size() const noexcept;
64+
65+
/**
66+
* @brief Getter for the prefix address
67+
* @return const IpAddress& prefix address
68+
*/
69+
const IpAddress& getPrefix() const noexcept { return m_prefixAddress; }
70+
71+
/**
72+
* @brief Getter for the prefix mask
73+
* @return const IpAddress& prefix mask
74+
*/
75+
const IpAddress& getMask() const noexcept { return m_prefixMask; }
76+
77+
/**
78+
* @brief Match IP prefix and check if IP address in part of the prefix
79+
* @param ipAddress to be matched
80+
* @return true if ipAddress is in this prefix
81+
* @return false otherwise
82+
*/
83+
bool match(const IpAddress& ipAddress) const noexcept;
84+
85+
/**
86+
* @brief Comparison operator <
87+
* @param l left operand
88+
* @param r right operand
89+
* @return bool true if l is less than r
90+
*/
91+
friend bool operator<(const IpPrefix& l, const IpPrefix& r);
92+
93+
/**
94+
* @brief Comparison operator <
95+
* Used for binary search of IP address in a vector of IP prefixes
96+
* @param l left operand
97+
* @param r right operand
98+
* @return bool true if l is less than r
99+
*/
100+
friend bool operator<(const IpAddress& l, const IpPrefix& r);
101+
102+
/**
103+
* @brief Comparison operator <
104+
* Used for binary search of IP address in a vector of IP prefixes
105+
* @param l left operand
106+
* @param r right operand
107+
* @return bool true if l is less than r
108+
*/
109+
friend bool operator<(const IpPrefix& l, const IpAddress& r);
110+
111+
private:
112+
IpAddress m_prefixAddress;
113+
IpAddress m_prefixMask;
114+
size_t m_prefixLength;
115+
};
116+
117+
} // namespace WIF

src/wif/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set(LIBWIF_SOURCES
22
classifiers/classifier.cpp
3+
classifiers/ipPrefixClassifier.cpp
34
classifiers/regexClassifier.cpp
45
classifiers/scikitMlClassifier.cpp
56
combinators/averageCombinator.cpp
@@ -10,6 +11,7 @@ set(LIBWIF_SOURCES
1011
regex/regexPattern.cpp
1112
storage/flowFeatures.cpp
1213
storage/ipAddress.cpp
14+
utils/ipPrefix.cpp
1315
)
1416

1517
set(LIBWIF_LIBS
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @file
3+
* @author Richard Plny <plnyrich@fit.cvut.cz>
4+
* @brief IP prefix classifier implementation
5+
*
6+
* SPDX-License-Identifier: BSD-3-Clause
7+
*/
8+
9+
#include "wif/classifiers/ipPrefixClassifier.hpp"
10+
11+
namespace WIF {
12+
13+
IpPrefixClassifier::IpPrefixClassifier(const std::vector<IpPrefix>& blocklist)
14+
{
15+
updateBlocklist(blocklist);
16+
}
17+
18+
void IpPrefixClassifier::updateBlocklist(const std::vector<IpPrefix>& blocklist)
19+
{
20+
m_blocklist = blocklist;
21+
std::sort(m_blocklist.begin(), m_blocklist.end());
22+
}
23+
24+
ClfResult IpPrefixClassifier::classify(const FlowFeatures& flowFeatures)
25+
{
26+
for (FeatureID featureID : getSourceFeatureIDs()) {
27+
auto ipAddress = flowFeatures.get<IpAddress>(featureID);
28+
if (findIpAddress(ipAddress)) {
29+
return ClfResult(1.0);
30+
}
31+
}
32+
return ClfResult(0.0);
33+
}
34+
35+
std::vector<ClfResult>
36+
IpPrefixClassifier::classify(const std::vector<FlowFeatures>& burstOfFlowsFeatures)
37+
{
38+
std::vector<ClfResult> burstResults;
39+
burstResults.reserve(burstOfFlowsFeatures.size());
40+
for (const auto& flowFeatures : burstOfFlowsFeatures) {
41+
burstResults.emplace_back(classify(flowFeatures));
42+
}
43+
return burstResults;
44+
}
45+
46+
bool IpPrefixClassifier::findIpAddress(const IpAddress& ipAddress)
47+
{
48+
return std::binary_search(m_blocklist.begin(), m_blocklist.end(), ipAddress);
49+
}
50+
51+
} // namespace WIF

src/wif/storage/ipAddress.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ bool IpAddress::empty() const noexcept
6464
return false;
6565
}
6666

67+
IpAddress::IpVersion IpAddress::getVersion() const noexcept
68+
{
69+
return isIPv4() ? IpVersion::V4 : IpVersion::V6;
70+
}
71+
6772
bool IpAddress::isIPv4() const noexcept
6873
{
6974
return (m_ipData.ui64[0] == 0 && m_ipData.ui32[3] == IPV4_FILLING_CONSTANT);
@@ -124,6 +129,46 @@ IpAddress operator&(const IpAddress& l, const IpAddress& r)
124129
return result;
125130
}
126131

132+
IpAddress operator~(const IpAddress& address)
133+
{
134+
IpAddress result;
135+
for (unsigned ui64Idx = 0; ui64Idx < IpAddress::SIZE_IN_UINT64; ++ui64Idx) {
136+
result.m_ipData.ui64[ui64Idx] = ~(address.m_ipData.ui64[ui64Idx]);
137+
}
138+
return result;
139+
}
140+
141+
bool operator<(const IpAddress& l, const IpAddress& r)
142+
{
143+
// All IPv4 are before IPv6
144+
if (l.isIPv4() && r.isIPv6()) {
145+
return true;
146+
} else if (l.isIPv6() && r.isIPv4()) {
147+
return false;
148+
}
149+
150+
if (l.isIPv4()) {
151+
return l.compareV4(r);
152+
} else {
153+
return l.compareV6(r);
154+
}
155+
}
156+
157+
bool IpAddress::compareV4(const IpAddress& other) const
158+
{
159+
return v4AsInt() < other.v4AsInt();
160+
}
161+
162+
bool IpAddress::compareV6(const IpAddress& other) const
163+
{
164+
for (unsigned byteIdx = 0; byteIdx < SIZE_IN_UINT8; ++byteIdx) {
165+
if (m_ipData.ui8[byteIdx] != other.m_ipData.ui8[byteIdx]) {
166+
return m_ipData.ui8[byteIdx] < other.m_ipData.ui8[byteIdx];
167+
}
168+
}
169+
return false;
170+
}
171+
127172
void IpAddress::createV4FromBytes(const uint8_t* bytes, bool isLittleEndian)
128173
{
129174
if (isLittleEndian) {

0 commit comments

Comments
 (0)