blob: 4f6894290b40664d6421176a6ab6a6b99a0f371a [file] [log] [blame]
maojianwei42e23442016-02-15 10:40:48 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
maojianwei42e23442016-02-15 10:40:48 +08003 *
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.fnl.base;
17
18import org.onosproject.fnl.intf.NetworkAnomaly;
19import org.onosproject.net.DeviceId;
20import org.onosproject.net.Link;
21import org.onosproject.net.flow.FlowEntry;
22import org.onosproject.net.flow.criteria.Criterion;
23import org.onosproject.net.flow.criteria.EthCriterion;
24import org.onosproject.net.flow.criteria.EthTypeCriterion;
25import org.onosproject.net.flow.criteria.IPCriterion;
26import org.onosproject.net.flow.criteria.IPDscpCriterion;
27import org.onosproject.net.flow.criteria.IPEcnCriterion;
28import org.onosproject.net.flow.criteria.IPProtocolCriterion;
29import org.onosproject.net.flow.criteria.PortCriterion;
30import org.onosproject.net.flow.criteria.TcpPortCriterion;
31import org.onosproject.net.flow.criteria.UdpPortCriterion;
32import org.onosproject.net.flow.criteria.VlanIdCriterion;
33import org.onosproject.net.flow.criteria.VlanPcpCriterion;
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40import java.util.Stack;
41import java.util.ArrayList;
42import java.util.Iterator;
43import java.util.Collections;
44
45import static org.onosproject.fnl.intf.NetworkAnomaly.Type.LOOP;
46import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_FAILURE_NULL;
47import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_OVERRIDE;
48import static org.onosproject.fnl.base.TsLoopPacket.SetHeaderResult.SETHEADER_SUCCESS;
49import static org.onosproject.net.flow.criteria.Criteria.*;
50
51/**
52 * Virtual packet for Default Loop Checking.
53 */
54public 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 *
283 * @param deviceId identify of the divice to test
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}