blob: d628f8027d0e160888752f47437c584644236986 [file] [log] [blame]
Harshada Chaundkardcd1b142019-03-25 17:27:44 -04001/*
2 * Copyright 2019-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.net.packet.packetfilter;
17
18import org.onosproject.net.packet.PacketContext;
19import org.onosproject.net.packet.PacketInClassifier;
20import org.onosproject.net.packet.PacketInFilter;
21import org.slf4j.Logger;
22import org.slf4j.LoggerFactory;
23
24import java.util.Objects;
25import java.util.concurrent.Executors;
26import java.util.concurrent.ScheduledExecutorService;
27import java.util.concurrent.TimeUnit;
28import java.util.concurrent.atomic.AtomicInteger;
29
30import static org.onlab.util.Tools.groupedThreads;
31
32/**
33 * Default implementation of a packet-in filter.
34 */
35public class DefaultPacketInFilter implements PacketInFilter {
36
37 /**
38 * Tracks the count of specific packet types (eg ARP, ND, DHCP etc)
39 * to be limited in the packet queue. This count always reflects the
40 * number of packets in the queue at any point in time
41 */
42 private AtomicInteger currentCounter = new AtomicInteger(0);
43
44 /**
45 * Tracks the number of continuous windows where the drop packet happens.
46 */
47 private AtomicInteger windowBlockCounter = new AtomicInteger(0);
48
49 /**
50 * Tracks the counter of the packet which are dropped.
51 */
52 private AtomicInteger overFlowCounter = new AtomicInteger(0);
53
54 private final Logger log = LoggerFactory.getLogger(getClass());
55
56 /**
57 * Max Allowed packet rate beyond which the packet will be dropped
58 * within given window size.
59 */
60 private int pps = 100;
61
62 /**
63 * Window size which will be used for number of packets acceptable
64 * based on the accepted pps.
65 */
66 private int winSize = 500;
67
68 /**
69 * Guard time in seconds which will be enabled if there are continuous
70 * windows crossing winThres where the packet rate crosses the acceptable
71 * packet count calculated based on accepted pps.
72 * Guard time should be always greater then the the window size.
73 */
74 private int guardTime = 10;
75
76 /**
77 * Threshold of continuous windows where the packet drop happens post which
78 * the guardTime will be triggered and no future packet processing happens
79 * till the expiry of this guard time.
80 */
81 private int winThres = 10;
82
83
84 private int maxCounter;
85
86 private ScheduledExecutorService timerExecutor;
87
88 private ScheduledExecutorService windowUnblockExecutor;
89
90 private boolean windowBlocked;
91
92 private boolean packetProcessingBlocked;
93
94
95 /**
96 * Name of the counter.
97 */
98 private String counterName;
99
100 /**
101 * PacketInclassifier associated with this filter object.
102 */
103 private final PacketInClassifier classifier;
104
105
106
107 /**
108 * Only one filter object per packet type to be associated.
109 * Multiple filter types will result in undefined behavior.
110 * @param pps Rate at which the packet is accepted in packets per second
111 * @param winSize Size of window in milli seconds within which
112 * the packet rate will be analyzed
113 * @param guardTime Time duration in seconds for which the packet processing
114 * will be on hold if there is a continuous window where
115 * cross of the rate happens and that window count crosses
116 * winThres
117 * @param winThres Continuous window threshold after which gaurdTime will be
118 * activated
119 * @param counterName Name of the counter
120 * @param classifier Packet classification
121 */
122 public DefaultPacketInFilter(int pps, int winSize, int guardTime, int winThres,
123 String counterName, PacketInClassifier classifier) {
124 this.pps = pps;
125 this.winSize = winSize;
126 this.guardTime = guardTime;
127 this.winThres = winThres;
128 this.counterName = counterName;
129 this.classifier = classifier;
130 this.maxCounter = (pps * winSize) / 1000;
131 timerExecutor = Executors.newScheduledThreadPool(1,
132 groupedThreads("packet/packetfilter",
133 "packet-filter-timer-%d", log));
134
135 windowUnblockExecutor = Executors.newScheduledThreadPool(1,
136 groupedThreads("packet/packetfilter",
137 "packet-filter-unblocker-%d", log));
138 timerExecutor.scheduleAtFixedRate(new ClearWindowBlock(),
139 0,
140 winSize,
141 TimeUnit.MILLISECONDS);
142
143
144 windowBlocked = false;
145 packetProcessingBlocked = false;
146
147 }
148
149
150
151 @Override
152 public FilterAction preProcess(PacketContext packet) {
153
154
155 maxCounter = (pps * winSize) / 1000;
156
157 // If pps is set then min value for maxCounter is 1
158 if (maxCounter == 0 && pps != 0) {
159 log.trace("{}: maxCounter set to 1 as was coming as 0", counterName);
160 maxCounter = 1;
161 }
162
163
164
165 if (!classifier.match(packet)) {
166 return FilterAction.FILTER_INVALID;
167 }
168
169 if (pps == 0 && maxCounter == 0) {
170 log.trace("{}: Filter is disabled", counterName);
171 return FilterAction.FILTER_DISABLED;
172 }
173 log.trace("{}: Preprocess called", counterName);
174
175 // Packet block checking should be done before windowBlocked checking
176 // otherwise there will be windows with packets while packet processing is suspended
177 // and that may break the existing check logic
178 if (packetProcessingBlocked) {
179 log.trace("{}: Packet processing is blocked for sometime", counterName);
180 return FilterAction.PACKET_BLOCKED;
181 }
182
183 if (windowBlocked) {
184 log.trace("{}: Packet processing is blocked for the window number: {}",
185 counterName, windowBlockCounter.get());
186 return FilterAction.WINDOW_BLOCKED;
187 }
188
189 if (currentCounter.getAndIncrement() < maxCounter) {
190 log.trace("{}: Packet is picked for processing with currentCounter: {} and maxCounter: {}",
191 counterName, currentCounter.get(), maxCounter);
192 return FilterAction.PACKET_ALLOW;
193 }
194 //Need to decrement the currentCounter and increment overFlowCounter
195 //Need to block the window and increment the window block counter
196 windowBlocked = true;
197 //TODO: Review this and the complete state machine
198 // If windowBlock crosses threshold then block packet processing for guard time
199 if (windowBlockCounter.incrementAndGet() > winThres) {
200 log.trace("{}: Packet processing blocked as current window crossed threshold " +
201 "currentWindowNumber: {} maxWindowNumber: {}",
202 counterName, windowBlockCounter.get(), winThres);
203 packetProcessingBlocked = true;
204 windowUnblockExecutor.schedule(new ClearPacketProcessingBlock(),
205 guardTime,
206 TimeUnit.SECONDS);
207 } else {
208 log.trace("{}: WindowBlockCounter: {} winThres: {}", counterName, windowBlockCounter.get(),
209 winThres);
210 }
211 //MT: Temp change in logic to branch the code - Rolled back
212 currentCounter.decrementAndGet();
213 if (overFlowCounter.incrementAndGet() < 0) {
214 overFlowCounter.set(0);
215 }
216 log.trace("{}: Overflow counter is: {}", counterName, overFlowCounter.get());
217 return FilterAction.PACKET_DENY;
218
219 }
220
221 @Override
222 public String name() {
223 return counterName;
224 }
225
226 @Override
227 public int pendingPackets() {
228 return currentCounter.get();
229 }
230
231 @Override
232 public int droppedPackets() {
233 return overFlowCounter.get();
234 }
235
236
237
238 @Override
239 public void setPps(int pps) {
240 this.pps = pps;
241 }
242
243 @Override
244 public void setWinSize(int winSize) {
245 this.winSize = winSize;
246 }
247
248 @Override
249 public void setGuardTime(int guardTime) {
250 this.guardTime = guardTime;
251 }
252
253 @Override
254 public void setWinThres(int winThres) {
255 this.winThres = winThres;
256 }
257
258 @Override
259 public void stop() {
260 timerExecutor.shutdown();
261 windowUnblockExecutor.shutdown();
262 }
263
264 @Override
265 public boolean equals(Object o) {
266 if (this == o) {
267 return true;
268 }
269 if (o == null || getClass() != o.getClass()) {
270 return false;
271 }
272 DefaultPacketInFilter that = (DefaultPacketInFilter) o;
273 return pps == that.pps &&
274 winSize == that.winSize &&
275 guardTime == that.guardTime &&
276 winThres == that.winThres &&
277 counterName.equals(that.counterName) &&
278 classifier.equals(that.classifier);
279 }
280
281 @Override
282 public int hashCode() {
283 return Objects.hash(pps, winSize, guardTime, winThres, counterName, classifier);
284 }
285
286
287 private final class ClearWindowBlock implements Runnable {
288 @Override
289 public void run() {
290 // If window is not already blocked and there is at least one packet processed
291 // in that window then reset the window block counter:
292 if (!windowBlocked) {
293 log.trace("{}: WindowBlockCounter is reset as there was no blocking in current " +
294 "window with current windowBlockCounter: {}", counterName, windowBlockCounter.get());
295 windowBlockCounter.set(0);
296 }
297 if (currentCounter.get() == 0) {
298 //No packet processed in current window so do not change anything in the current state
299 log.trace("{}: No packets in the current window so not doing anything in ClearWindowBlock",
300 counterName);
301 return;
302 }
303
304 //Reset the counter and unblock the window
305 log.trace("{}: Current counter and windowBlocked is reset in ClearWindowBlock", counterName);
306 currentCounter.set(0);
307 windowBlocked = false;
308 }
309 }
310
311 private final class ClearPacketProcessingBlock implements Runnable {
312 @Override
313 public void run() {
314 //Reset the counter and unblock the window and packet processing
315 //CurrentCounter and windowBlocked counter setting is not required here
316 //Still setting to be on safer side
317 log.trace("{}: All blocks cleared in ClearPacketProcessingBlock", counterName);
318 currentCounter.set(0);
319 windowBlocked = false;
320 packetProcessingBlocked = false;
321 windowBlockCounter.set(0);
322 }
323 }
324}