maojianwei | 42e2344 | 2016-02-15 10:40:48 +0800 | [diff] [blame] | 1 | /* |
Brian O'Connor | a09fe5b | 2017-08-03 21:12:30 -0700 | [diff] [blame] | 2 | * Copyright 2015-present Open Networking Foundation |
maojianwei | 42e2344 | 2016-02-15 10:40:48 +0800 | [diff] [blame] | 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.fnl.base; |
| 17 | |
| 18 | import org.onosproject.fnl.intf.NetworkAnomaly; |
| 19 | import org.onosproject.net.DeviceId; |
| 20 | import org.onosproject.net.Link; |
| 21 | import org.onosproject.net.flow.FlowEntry; |
| 22 | import org.onosproject.net.flow.criteria.Criterion; |
| 23 | import org.onosproject.net.flow.criteria.EthCriterion; |
| 24 | import org.onosproject.net.flow.criteria.EthTypeCriterion; |
| 25 | import org.onosproject.net.flow.criteria.IPCriterion; |
| 26 | import org.onosproject.net.flow.criteria.IPDscpCriterion; |
| 27 | import org.onosproject.net.flow.criteria.IPEcnCriterion; |
| 28 | import org.onosproject.net.flow.criteria.IPProtocolCriterion; |
| 29 | import org.onosproject.net.flow.criteria.PortCriterion; |
| 30 | import org.onosproject.net.flow.criteria.TcpPortCriterion; |
| 31 | import org.onosproject.net.flow.criteria.UdpPortCriterion; |
| 32 | import org.onosproject.net.flow.criteria.VlanIdCriterion; |
| 33 | import org.onosproject.net.flow.criteria.VlanPcpCriterion; |
| 34 | import org.slf4j.Logger; |
| 35 | import org.slf4j.LoggerFactory; |
| 36 | |
| 37 | import java.util.HashMap; |
| 38 | import java.util.List; |
| 39 | import java.util.Map; |
| 40 | import java.util.Stack; |
| 41 | import java.util.ArrayList; |
| 42 | import java.util.Iterator; |
| 43 | import java.util.Collections; |
| 44 | |
| 45 | import static org.onosproject.fnl.intf.NetworkAnomaly.Type.LOOP; |
| 46 | import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_FAILURE_NULL; |
| 47 | import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_OVERRIDE; |
| 48 | import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_SUCCESS; |
| 49 | import static org.onosproject.net.flow.criteria.Criteria.*; |
| 50 | |
| 51 | /** |
| 52 | * Virtual packet for Default Loop Checking. |
| 53 | */ |
| 54 | public final class TsLoopPacket implements NetworkAnomaly { |
| 55 | |
| 56 | private final Logger log = LoggerFactory.getLogger(getClass()); |
| 57 | |
| 58 | private static final String EOL = String.format("%n"); |
| 59 | private static final String LINE = |
| 60 | EOL + "====================================================" + EOL; |
| 61 | private static final String HDR_FMT = EOL + "---------- %s ----------" + EOL; |
| 62 | private static final String LOOP_HEADER = makeHeader("Loop Header"); |
| 63 | private static final String LOOP_FLOW_ENTRIES = makeHeader("Loop Flow Entries"); |
| 64 | private static final String LOOP_LINKS = makeHeader("Loop Links"); |
| 65 | |
| 66 | private Map<Criterion.Type, Criterion> match; |
| 67 | private Stack<FlowEntry> pathFlow; |
| 68 | // when Upgrade, check to MAKE SURE it include just Link but not EdgeLink |
| 69 | private Stack<Link> pathLink; |
| 70 | |
| 71 | /** |
| 72 | * Create an initial virtual packet inside for Loop Checking. |
| 73 | */ |
| 74 | private TsLoopPacket() { |
| 75 | match = new HashMap<>(); |
| 76 | pathFlow = new Stack<>(); |
| 77 | pathLink = new Stack<>(); |
| 78 | } |
| 79 | |
| 80 | @Override |
| 81 | public Type type() { |
| 82 | return LOOP; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * Represents the result of setting a header to virtual packet. |
| 87 | */ |
| 88 | public enum SetHeaderResult { |
| 89 | /** |
| 90 | * Set header successfully. |
| 91 | */ |
| 92 | SETHEADER_SUCCESS, |
| 93 | |
| 94 | /** |
| 95 | * Set header successfully but override old value. |
| 96 | */ |
| 97 | SETHEADER_OVERRIDE, |
| 98 | |
| 99 | /** |
| 100 | * Fail to set Header because NULL value. |
| 101 | */ |
| 102 | SETHEADER_FAILURE_NULL, |
| 103 | |
| 104 | /** |
| 105 | * Fail to set Header, but reason is not defined, defined in advance. |
| 106 | */ |
| 107 | SETHEADER_FAILURE |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Creates and returns a new packet instance with the copied match fields. |
| 112 | * |
| 113 | * With hard-copied match fields, references to path flows and path links. |
| 114 | * |
| 115 | * @return new loop packet instance with the copied match fields |
| 116 | */ |
| 117 | public TsLoopPacket copyPacketMatch() { |
| 118 | |
| 119 | TsLoopPacket newOne = new TsLoopPacket(); |
| 120 | |
| 121 | newOne.pathFlow = this.pathFlow; |
| 122 | newOne.pathLink = this.pathLink; |
| 123 | |
| 124 | Map<Criterion.Type, Criterion> m = newOne.match; |
| 125 | |
| 126 | for (Map.Entry<Criterion.Type, Criterion> entry : this.match.entrySet()) { |
| 127 | Criterion.Type k = entry.getKey(); |
| 128 | Criterion v = entry.getValue(); |
| 129 | |
| 130 | switch (k) { |
| 131 | case IN_PORT: |
| 132 | m.put(k, matchInPort(((PortCriterion) v).port())); |
| 133 | break; |
| 134 | case ETH_SRC: // At present, not support Ethernet mask (ONOS?) |
| 135 | m.put(k, matchEthSrc(((EthCriterion) v).mac())); |
| 136 | break; |
| 137 | case ETH_DST: // At present, not support Ethernet mask (ONOS?) |
| 138 | m.put(k, matchEthDst(((EthCriterion) v).mac())); |
| 139 | break; |
| 140 | case ETH_TYPE: |
| 141 | m.put(k, matchEthType(((EthTypeCriterion) v).ethType())); |
| 142 | break; |
| 143 | case VLAN_VID: // At present, not support VLAN mask (ONOS?) |
| 144 | m.put(k, matchVlanId(((VlanIdCriterion) v).vlanId())); |
| 145 | break; |
| 146 | case VLAN_PCP: |
| 147 | m.put(k, matchVlanPcp(((VlanPcpCriterion) v).priority())); |
| 148 | break; |
| 149 | case IPV4_SRC: |
| 150 | m.put(k, matchIPSrc(((IPCriterion) v).ip())); |
| 151 | break; |
| 152 | case IPV4_DST: |
| 153 | m.put(k, matchIPDst(((IPCriterion) v).ip())); |
| 154 | break; |
| 155 | case IP_PROTO: |
| 156 | m.put(k, matchIPProtocol(((IPProtocolCriterion) v).protocol())); |
| 157 | break; |
| 158 | case IP_DSCP: // can't be supported by now |
| 159 | m.put(k, matchIPDscp(((IPDscpCriterion) v).ipDscp())); |
| 160 | break; |
| 161 | case IP_ECN: // can't be supported by now |
| 162 | m.put(k, matchIPEcn(((IPEcnCriterion) v).ipEcn())); |
| 163 | break; |
| 164 | case TCP_SRC: |
| 165 | m.put(k, matchTcpSrc(((TcpPortCriterion) v).tcpPort())); |
| 166 | break; |
| 167 | case TCP_DST: |
| 168 | m.put(k, matchTcpDst(((TcpPortCriterion) v).tcpPort())); |
| 169 | break; |
| 170 | case UDP_SRC: |
| 171 | m.put(k, matchUdpSrc(((UdpPortCriterion) v).udpPort())); |
| 172 | break; |
| 173 | case UDP_DST: |
| 174 | m.put(k, matchUdpDst(((UdpPortCriterion) v).udpPort())); |
| 175 | break; |
| 176 | default: //can't be supported by OF1.0 |
| 177 | log.debug("{} can't be supported by OF1.0", k); |
| 178 | break; |
| 179 | } |
| 180 | } |
| 181 | return newOne; |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Sets the given criterion as a packet header field. |
| 186 | * |
| 187 | * @param criterion as packet header field |
| 188 | * @return the result of set action |
| 189 | */ |
| 190 | public SetHeaderResult setHeader(Criterion criterion) { |
| 191 | |
| 192 | if (criterion == null) { |
| 193 | return SETHEADER_FAILURE_NULL; |
| 194 | } |
| 195 | |
| 196 | boolean hasKey = match.containsKey(criterion.type()); |
| 197 | |
| 198 | match.put(criterion.type(), criterion); |
| 199 | |
| 200 | return hasKey ? SETHEADER_OVERRIDE : SETHEADER_SUCCESS; |
| 201 | } |
| 202 | |
| 203 | /** |
| 204 | * Deletes a packet header field by the designated header type. |
| 205 | * |
| 206 | * @param criterionType as packet header type |
| 207 | * @return true, if packet contained the corresponding type of header; |
| 208 | * false, otherwise |
| 209 | */ |
| 210 | public boolean delHeader(Criterion.Type criterionType) { |
| 211 | return match.remove(criterionType) != null; |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Returns a packet header field value by the designated header type. |
| 216 | * |
| 217 | * Returns null if the field does not exist. |
| 218 | * |
| 219 | * @param criterionType as packet header type |
| 220 | * @return the packet header field value; may be null |
| 221 | */ |
| 222 | public Criterion getHeader(Criterion.Type criterionType) { |
| 223 | return match.get(criterionType); |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Returns true if there is the type of header field in the packet. |
| 228 | * |
| 229 | * @param criterionType packet header type |
| 230 | * @return true if the field exists; false otherwise |
| 231 | */ |
| 232 | public boolean headerExists(Criterion.Type criterionType) { |
| 233 | return match.containsKey(criterionType); |
| 234 | } |
| 235 | |
| 236 | /** |
| 237 | * Pushes the given flow entry onto the path flow stack. |
| 238 | * Packet matches this entry in specific switch hop. |
| 239 | * |
| 240 | * @param entry the matched entry |
| 241 | */ |
| 242 | public void pushPathFlow(FlowEntry entry) { |
| 243 | pathFlow.push(entry); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Pops a FlowEntry from path flow entry stack. |
| 248 | */ |
| 249 | public void popPathFlow() { |
| 250 | pathFlow.pop(); |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Returns links in the path which the packet passes through. |
| 255 | * |
| 256 | * @return an iterator over the set of links |
| 257 | */ |
| 258 | public Iterator<Link> getPathLink() { |
| 259 | return pathLink.iterator(); |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Adds a Link to path link list. |
| 264 | * Packet goes through this link between two switches. |
| 265 | * |
| 266 | * @param link The link through which the packet go |
| 267 | */ |
| 268 | public void pushPathLink(Link link) { |
| 269 | // TODO - need CPY link manual? |
| 270 | pathLink.push(link); |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Removes a Link from path link list. |
| 275 | */ |
| 276 | public void popPathLink() { |
| 277 | pathLink.pop(); |
| 278 | } |
| 279 | |
| 280 | /** |
| 281 | * Returns true if the packet passed through the specific device. |
| 282 | * |
Ray Milkey | c108a6b | 2017-08-23 15:23:50 -0700 | [diff] [blame] | 283 | * @param deviceId identify of the device to test |
maojianwei | 42e2344 | 2016-02-15 10:40:48 +0800 | [diff] [blame] | 284 | * @return true if packet passed through the specific device; |
| 285 | * false otherwise |
| 286 | */ |
| 287 | public boolean isPassedDevice(DeviceId deviceId) { |
| 288 | for (Link linkTemp : pathLink) { |
| 289 | if (deviceId.equals(linkTemp.src().deviceId())) { |
| 290 | return true; |
| 291 | } |
| 292 | } |
| 293 | return false; |
| 294 | } |
| 295 | |
| 296 | /** |
| 297 | * Returns the IN_PORT header field of the packet. |
| 298 | * |
| 299 | * Attention: |
| 300 | * IN_PORT field will be changed when packet goes into the next switch hop. |
| 301 | * |
| 302 | * @return a port criterion object. |
| 303 | */ |
| 304 | public PortCriterion getInport() { |
| 305 | // TODO - check IN_PORT or IN_PHY_PORT |
| 306 | return (PortCriterion) match.get(Criterion.Type.IN_PORT); |
| 307 | } |
| 308 | |
| 309 | /** |
| 310 | * Creates and returns a loop packet instance with given Match Fields. |
| 311 | * |
| 312 | * Returns null, |
| 313 | * whenever SetHeader_FAILURE or SETHEADER_FAILURE_NULL happened. |
| 314 | * |
| 315 | * @param criteria match field of one flow entry |
| 316 | * @param collision as return value; |
| 317 | * true, if criteria contain multiple ones with same type |
| 318 | * @return a new loop packet instance; may be null |
| 319 | */ |
| 320 | public static TsLoopPacket matchBuilder(Iterable<Criterion> criteria, |
| 321 | TsReturn<Boolean> collision) { |
| 322 | |
| 323 | if (null != collision) { |
| 324 | collision.setValue(false); |
| 325 | } |
| 326 | |
| 327 | TsLoopPacket pkt = new TsLoopPacket(); |
| 328 | |
| 329 | for (Criterion criterion : criteria) { |
| 330 | |
| 331 | SetHeaderResult ret = pkt.setHeader(criterion); |
| 332 | |
| 333 | if (SETHEADER_SUCCESS == ret) { |
| 334 | //TODO - in the future, we may need to resolve this condition |
| 335 | } else if (SETHEADER_OVERRIDE == ret) { |
| 336 | if (null != collision) { |
| 337 | collision.setValue(true); |
| 338 | } |
| 339 | } else { // SetHeader_FAILURE or SetHeader_FAILURE_NULL |
| 340 | pkt = null; |
| 341 | break; |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | return pkt; |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Hands in the header of virtual packet one by one. |
| 350 | * Let the header go up through every layer of recursion. |
| 351 | * It is called when a loop is discovered. |
| 352 | * |
| 353 | * @param loopPkt virtual packet that will trigger Loop Storm |
| 354 | */ |
| 355 | public void handInLoopMatch(TsLoopPacket loopPkt) { |
| 356 | match = loopPkt.match; |
| 357 | } |
| 358 | |
| 359 | /** |
| 360 | * Resets the path link and path flow structures. |
| 361 | * And initializing the path flow with the gicen flow entry. |
| 362 | * |
| 363 | * @param firstEntry the flow entry from which this packet is built |
| 364 | */ |
| 365 | public void resetLinkFlow(FlowEntry firstEntry) { |
| 366 | pathLink = new Stack<>(); |
| 367 | pathFlow = new Stack<>(); |
| 368 | pathFlow.push(firstEntry); |
| 369 | } |
| 370 | |
| 371 | private static String makeHeader(String title) { |
| 372 | return String.format(HDR_FMT, title); |
| 373 | } |
| 374 | |
| 375 | /** |
| 376 | * Returns a multi-line string representation of this loop packet instance. |
| 377 | * |
| 378 | * @return formatted string |
| 379 | */ |
| 380 | @Override |
| 381 | public String toString() { |
| 382 | StringBuilder me = new StringBuilder(); |
| 383 | |
| 384 | me.append(LINE); |
| 385 | |
| 386 | me.append(LOOP_HEADER); |
| 387 | |
| 388 | List<Criterion> criteria = new ArrayList<>(match.values()); |
| 389 | Collections.sort(criteria, (o1, o2) -> o1.type().compareTo(o2.type())); |
| 390 | |
| 391 | for (Criterion c : criteria) { |
| 392 | me.append(c).append(EOL); |
| 393 | } |
| 394 | |
| 395 | me.append(LOOP_FLOW_ENTRIES); |
| 396 | |
| 397 | for (FlowEntry flow : pathFlow) { |
| 398 | me.append(flow).append(EOL); |
| 399 | } |
| 400 | |
| 401 | me.append(LOOP_LINKS); |
| 402 | |
| 403 | for (Link l : pathLink) { |
| 404 | me.append(l).append(EOL); |
| 405 | } |
| 406 | |
| 407 | return me.toString(); |
| 408 | } |
| 409 | } |