Harshada Chaundkar | dcd1b14 | 2019-03-25 17:27:44 -0400 | [diff] [blame] | 1 | /* |
| 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 | */ |
| 16 | package org.onosproject.net.packet.packetfilter; |
| 17 | |
| 18 | import org.onosproject.net.packet.PacketContext; |
| 19 | import org.onosproject.net.packet.PacketInClassifier; |
| 20 | import org.onosproject.net.packet.PacketInFilter; |
| 21 | import org.slf4j.Logger; |
| 22 | import org.slf4j.LoggerFactory; |
| 23 | |
| 24 | import java.util.Objects; |
| 25 | import java.util.concurrent.Executors; |
| 26 | import java.util.concurrent.ScheduledExecutorService; |
| 27 | import java.util.concurrent.TimeUnit; |
| 28 | import java.util.concurrent.atomic.AtomicInteger; |
| 29 | |
| 30 | import static org.onlab.util.Tools.groupedThreads; |
| 31 | |
| 32 | /** |
| 33 | * Default implementation of a packet-in filter. |
| 34 | */ |
| 35 | public 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 | } |