forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBasicPacketFilters.h
185 lines (162 loc) · 7.46 KB
/
BasicPacketFilters.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* Copyright (c) 2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <atomic>
#include <inet/EndpointQueueFilter.h>
#include <inet/IPPacketInfo.h>
#include <lib/support/CodeUtils.h>
#include <system/SystemPacketBuffer.h>
namespace chip {
namespace Inet {
/**
* @brief Basic filter that counts how many pending (not yet dequeued) packets
* are accumulated that match a predicate function, and drops those that
* would cause crossing of the threshold.
*/
class DropIfTooManyQueuedPacketsFilter : public chip::Inet::EndpointQueueFilter
{
public:
typedef bool (*PacketMatchPredicateFunc)(void * context, const void * endpoint, const chip::Inet::IPPacketInfo & pktInfo,
const chip::System::PacketBufferHandle & pktPayload);
/**
* @brief Initialize the packet filter with a starting limit
*
* @param maxAllowedQueuedPackets - max number of pending-in-queue not yet processed predicate-matching packets
*/
DropIfTooManyQueuedPacketsFilter(size_t maxAllowedQueuedPackets) : mMaxAllowedQueuedPackets(maxAllowedQueuedPackets) {}
/**
* @brief Set the predicate to use for filtering
*
* @warning DO NOT modify at runtime while the filter is being called. If you do so, the queue accounting could
* get out of sync, and cause the filtering to fail to properly work.
*
* @param predicateFunc - Predicate function to apply. If nullptr, no filtering will take place
* @param context - Pointer to predicate-specific context that will be provided to predicate at every call. May be nullptr.
*/
void SetPredicate(PacketMatchPredicateFunc predicateFunc, void * context)
{
mPredicate = predicateFunc;
mContext = context;
}
/**
* @brief Set the ceiling for max allowed packets queued up that matched the predicate.
*
* @note Changing this at runtime while packets are coming only affects future dropping, and
* does not remove packets from the queue if the limit is lowered below the currently-in-queue
* count.
*
* @param maxAllowedQueuedPackets - number of packets currently pending allowed.
*/
void SetMaxQueuedPacketsLimit(size_t maxAllowedQueuedPackets) { mMaxAllowedQueuedPackets.store(maxAllowedQueuedPackets); }
/**
* @return the total number of packets dropped so far by the filter
*/
size_t GetNumDroppedPackets() const { return mNumDroppedPackets.load(); }
/**
* @brief Reset the counter of dropped packets.
*/
void ClearNumDroppedPackets() { mNumDroppedPackets.store(0); }
/**
* @brief Method called when a packet is dropped due to high watermark getting reached, based on predicate.
*
* Subclasses may use this to implement additional behavior or diagnostics.
*
* This is called once for every dropped packet. If there is no filter predicate, this is not called.
*
* @param endpoint - pointer to endpoint instance (platform-dependent, which is why it's void)
* @param pktInfo - info about source/dest of packet
* @param pktPayload - payload content of packet
*/
virtual void OnDropped(const void * endpoint, const chip::Inet::IPPacketInfo & pktInfo,
const chip::System::PacketBufferHandle & pktPayload)
{}
/**
* @brief Method called whenever queue of accumulated packets is now empty, based on predicate.
*
* Subclasses may use this to implement additional behavior or diagnostics.
*
* This is possibly called repeatedly in a row, if the queue actually never gets above one.
*
* This is only called for packets that had matched the filtering rule, where they had
* been explicitly allowed in the past. If there is no filter predicate, this is not called.
*
* @param endpoint - pointer to endpoint instance (platform-dependent, which is why it's void)
* @param pktInfo - info about source/dest of packet
* @param pktPayload - payload content of packet
*/
virtual void OnLastMatchDequeued(const void * endpoint, const chip::Inet::IPPacketInfo & pktInfo,
const chip::System::PacketBufferHandle & pktPayload)
{}
/**
* @brief Implementation of filtering before queueing that applies the predicate.
*
* See base class for arguments
*/
FilterOutcome FilterBeforeEnqueue(const void * endpoint, const chip::Inet::IPPacketInfo & pktInfo,
const chip::System::PacketBufferHandle & pktPayload) override
{
// WARNING: This is likely called in a different context than `FilterAfterDequeue`. We use an atomic for the counter.
// Non-matching is never accounted, always allowed. Lack of predicate is equivalent to non-matching.
if ((mPredicate == nullptr) || !mPredicate(mContext, endpoint, pktInfo, pktPayload))
{
return FilterOutcome::kAllowPacket;
}
if (mNumQueuedPackets.load() >= mMaxAllowedQueuedPackets)
{
++mNumDroppedPackets;
OnDropped(endpoint, pktInfo, pktPayload);
return FilterOutcome::kDropPacket;
}
++mNumQueuedPackets;
return FilterOutcome::kAllowPacket;
}
/**
* @brief Implementation of filtering after dequeueing that applies the predicate.
*
* See base class for arguments
*/
FilterOutcome FilterAfterDequeue(const void * endpoint, const chip::Inet::IPPacketInfo & pktInfo,
const chip::System::PacketBufferHandle & pktPayload) override
{
// WARNING: This is likely called in a different context than `FilterBeforeEnqueue`. We use an atomic for the counter.
// NOTE: This is always called from Matter platform event loop
// Non-matching is never accounted, always allowed. Lack of predicate is equivalent to non-matching.
if ((mPredicate == nullptr) || !mPredicate(mContext, endpoint, pktInfo, pktPayload))
{
return FilterOutcome::kAllowPacket;
}
// If we ever go negative, we have mismatch ingress/egress filter via predicate and
// device may eventually starve.
VerifyOrDie(mNumQueuedPackets != 0);
--mNumQueuedPackets;
size_t numQueuedPackets = mNumQueuedPackets.load();
if (numQueuedPackets == 0)
{
OnLastMatchDequeued(endpoint, pktInfo, pktPayload);
}
// We always allow the packet and just do accounting, since all dropping is prior to queue entry.
return FilterOutcome::kAllowPacket;
}
protected:
PacketMatchPredicateFunc mPredicate = nullptr;
void * mContext = nullptr;
std::atomic_size_t mNumQueuedPackets{ 0 };
std::atomic_size_t mMaxAllowedQueuedPackets{ 0 };
std::atomic_size_t mNumDroppedPackets{ 0u };
};
} // namespace Inet
} // namespace chip