Make sure invokeOperation is set once
[controller.git] / opendaylight / adsal / sal / api / src / main / java / org / opendaylight / controller / sal / match / Match.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.sal.match;
10
11 import java.io.Serializable;
12 import java.net.Inet4Address;
13 import java.net.Inet6Address;
14 import java.net.InetAddress;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.TreeMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ConcurrentHashMap;
26
27 import javax.xml.bind.annotation.XmlAccessType;
28 import javax.xml.bind.annotation.XmlAccessorType;
29 import javax.xml.bind.annotation.XmlElement;
30 import javax.xml.bind.annotation.XmlRootElement;
31
32 import org.opendaylight.controller.sal.core.Property;
33 import org.opendaylight.controller.sal.utils.EtherTypes;
34 import org.opendaylight.controller.sal.utils.IPProtocols;
35 import org.opendaylight.controller.sal.utils.NetUtils;
36
37 /**
38  * Represents the generic match criteria for a network frame/packet/message
39  * It contains a collection of individual field match
40  *
41  */
42 @XmlRootElement
43 @XmlAccessorType(XmlAccessType.NONE)
44 @Deprecated
45 public class Match implements Cloneable, Serializable {
46         private static final long serialVersionUID = 1L;
47         private static final Map<MatchType, MatchType> reversableMatches;
48     static {
49         Map<MatchType, MatchType> map = new HashMap<MatchType, MatchType>();
50         map.put(MatchType.DL_SRC, MatchType.DL_DST);
51         map.put(MatchType.DL_DST, MatchType.DL_SRC);
52         map.put(MatchType.NW_SRC, MatchType.NW_DST);
53         map.put(MatchType.NW_DST, MatchType.NW_SRC);
54         map.put(MatchType.TP_SRC, MatchType.TP_DST);
55         map.put(MatchType.TP_DST, MatchType.TP_SRC);
56         reversableMatches = Collections.unmodifiableMap(map);
57     }
58     private Map<MatchType, MatchField> fields;
59     private int matches; // concise way to tell which fields the match
60                          // is set for (may remove if not needed)
61     private ConcurrentMap<String, Property> props;
62
63     public Match() {
64         fields = new HashMap<MatchType, MatchField>();
65         matches = 0;
66     }
67
68     public Match(Match match) {
69         fields = new HashMap<MatchType, MatchField>(match.fields);
70         matches = match.matches;
71     }
72
73     /**
74      * Gets the list of metadata currently registered with this match
75      *
76      * @return List of metadata currently registered
77      */
78     public List <Property> getMetadatas() {
79         if (this.props != null) {
80             // Return all the values in the map
81             Collection res = this.props.values();
82             if (res == null) {
83                 return Collections.emptyList();
84             }
85             return new ArrayList<Property>(res);
86         }
87         return Collections.emptyList();
88     }
89
90     /**
91      * Gets the metadata registered with a name if present
92      *
93      * @param name the name of the property to be extracted
94      *
95      * @return List of metadata currently registered
96      */
97     public Property getMetadata(String name) {
98         if (name == null) {
99             return null;
100         }
101         if (this.props != null) {
102             // Return the Property associated to the name
103             return this.props.get(name);
104         }
105         return null;
106     }
107
108     /**
109      * Sets the metadata associated to a name. If the name or prop is NULL,
110      * an exception NullPointerException will be raised.
111      *
112      * @param name the name of the property to be set
113      * @param prop, property to be set
114      */
115     public void setMetadata(String name, Property prop) {
116         if (this.props == null) {
117             props = new ConcurrentHashMap<String, Property>();
118         }
119
120         if (this.props != null) {
121             this.props.put(name, prop);
122         }
123     }
124
125     /**
126      * Remove the metadata associated to a name. If the name is NULL,
127      * nothing will be removed.
128      *
129      * @param name the name of the property to be set
130      * @param prop, property to be set
131      *
132      * @return List of metadata currently registered
133      */
134     public void removeMetadata(String name) {
135         if (this.props == null) {
136             return;
137         }
138
139         if (this.props != null) {
140             this.props.remove(name);
141         }
142         // It's intentional to keep the this.props still allocated
143         // till the parent data structure will be alive, so to avoid
144         // unnecessary allocation/deallocation, even if it's holding
145         // nothing
146     }
147
148     /**
149      * Generic setter for frame/packet/message's header fields against which to match
150      * Note: For MAC addresses, please pass the cloned value to this function
151      *
152      * @param type      packet's header field type
153      * @param value     field's value to assign to the match
154      * @param mask      field's bitmask to apply to the match (has to be of the same class type of value)
155      */
156     public void setField(MatchType type, Object value, Object mask) {
157         MatchField field = new MatchField(type, value, mask);
158         if (field.isValid()) {
159             fields.put(type, field);
160             matches |= type.getIndex();
161         }
162     }
163
164     /**
165      * Generic setter for frame/packet/message's header fields against which to match
166      * Note: For MAC addresses, please pass the cloned value to this function
167      *
168      * @param type      packet's header field type
169      * @param value     field's value to assign to the match
170      */
171     public void setField(MatchType type, Object value) {
172         MatchField field = new MatchField(type, value);
173         if (field.isValid()) {
174             fields.put(type, field);
175             matches |= type.getIndex();
176         }
177     }
178
179     /**
180      * Generic setter for frame/packet/message's header field against which to match
181      *
182      * @param field the fields parameters as MAtchField object
183      */
184     public void setField(MatchField field) {
185         if (field.isValid()) {
186             fields.put(field.getType(), field);
187             matches |= field.getType().getIndex();
188         }
189     }
190
191     /**
192      * Generic method to clear a field from the match
193      */
194     public void clearField(MatchType type) {
195         fields.remove(type);
196         matches &= ~type.getIndex();
197     }
198
199     /**
200      * Generic getter for fields against which the match is programmed
201      *
202      * @param type  frame/packet/message's header field type
203      * @return
204      */
205     public MatchField getField(MatchType type) {
206         return fields.get(type);
207     }
208
209     /**
210      * Returns the fields the match is set for in a bitmask fashion
211      * Each bit represents a field the match is configured for
212      *
213      * @return the 32 bit long mask (Refer to {@code}org.opendaylight.controller.sal.match.MatchElement)
214      */
215     public int getMatches() {
216         return matches;
217     }
218
219     /**
220      * Returns the list of MatchType fields the match is set for
221      *
222      * @return List of individual MatchType fields.
223      */
224     public List<MatchType> getMatchesList() {
225         return new ArrayList<MatchType>(fields.keySet());
226     }
227
228     /**
229      * Returns the list of MatchFields the match is set for
230      *
231      * @return List of individual MatchField values.
232      */
233     @XmlElement(name="matchField")
234     public List<MatchField> getMatchFields() {
235         return new ArrayList<MatchField>(fields.values());
236     }
237
238     /**
239      * Returns whether this match is for an IPv6 flow
240      */
241     public boolean isIPv6() {
242         return (isPresent(MatchType.DL_TYPE)
243                 && ((Short) getField(MatchType.DL_TYPE).getValue())
244                         .equals(EtherTypes.IPv6.shortValue())
245                 || isPresent(MatchType.NW_PROTO)
246                 && ((Byte) getField(MatchType.NW_PROTO).getValue())
247                         .equals(IPProtocols.IPV6ICMP.byteValue())
248                 || isPresent(MatchType.NW_SRC)
249                 && getField(MatchType.NW_SRC).getValue() instanceof Inet6Address || isPresent(MatchType.NW_DST)
250                 && getField(MatchType.NW_DST).getValue() instanceof Inet6Address);
251     }
252
253     /**
254      * Returns whether this match is for an IPv4 flow
255      */
256     public boolean isIPv4() {
257         return !isIPv6();
258     }
259
260     /**
261      * Returns whether for the specified field type the match is to be considered "any"
262      * Equivalent to say this match does not care about the value of the specified field
263      *
264      * @param type
265      * @return
266      */
267     public boolean isAny(MatchType type) {
268         //return ((fields.get(type) == null) || (fields.get(type).getBitMask() == 0L));
269         return !fields.containsKey(type);
270     }
271
272     /**
273      * Returns whether a match for the specified field type is configured
274      *
275      * @param type
276      * @return
277      */
278     public boolean isPresent(MatchType type) {
279         return (fields.get(type) != null);
280     }
281
282     @Override
283     public Match clone() {
284         Match cloned = null;
285         try {
286             cloned = (Match) super.clone();
287             cloned.matches = matches;
288             cloned.fields = new HashMap<MatchType, MatchField>();
289             for (Entry<MatchType, MatchField> entry : this.fields.entrySet()) {
290                 cloned.fields.put(entry.getKey(), entry.getValue().clone());
291             }
292         } catch (CloneNotSupportedException e) {
293             throw new RuntimeException(e);
294         }
295         return cloned;
296     }
297
298     /**
299      * Returns a reversed version of this match
300      * For example, in the reversed version the network source and destination
301      * addresses will be exchanged. Non symmetric match field will not be
302      * copied over into the reversed match version, like input port.
303      *
304      * @return
305      */
306     public Match reverse() {
307         // Copy over all fields
308         Match reverse = this.clone();
309
310         // Flip symmetric fields
311         for (Map.Entry<MatchType, MatchType> entry : Match.reversableMatches.entrySet()) {
312             MatchType from = entry.getKey();
313             MatchType to = entry.getValue();
314             if (this.isPresent(from)) {
315                 reverse.setField(to, this.getField(from).getValue(), this.getField(from).getMask());
316                 if (!this.isPresent(to)) {
317                     reverse.clearField(from);
318                 }
319             }
320         }
321
322         // Reset asymmetric fields
323         reverse.clearField(MatchType.IN_PORT);
324
325         return reverse;
326     }
327
328     /**
329      * Check whether the current match conflicts with the passed filter match
330      * This match conflicts with the filter if for at least a MatchType defined
331      * in the filter match, the respective MatchFields differ or are not
332      * compatible
333      *
334      * In other words the function returns true if the set of packets described
335      * by one match and the set of packets described by the other match are
336      * disjoint. Equivalently, if the intersection of the two sets of packets
337      * described by the two matches is an empty.
338      *
339      * For example, Let's suppose the filter has the following MatchFields:
340      * DL_TYPE = 0x800
341      * NW_DST = 172.20.30.110/24
342      *
343      * while this match has the following MatchFields:
344      * DL_TYPE = 0x800
345      * NW_DST = 172.20.30.45/24
346      * TP_DST = 80
347      *
348      * Then the function would return false as the two Match are not
349      * conflicting.
350      *
351      * Note: the mask value is taken into account only for MatchType.NW_SRC and
352      * MatchType.NW_DST
353      *
354      * @param match
355      *            the Match describing the filter
356      * @return true if the set of packets described by one match and the set of
357      *         packets described by the other match are disjoint, false
358      *         otherwise
359      */
360     public boolean conflictWithFilter(Match filter) {
361         return !this.intersetcs(filter);
362     }
363
364     /**
365      * Merge the current Match fields with the fields of the filter Match. A
366      * check is first run to see if this Match is compatible with the filter
367      * Match. If it is not, the merge is not attempted.
368      *
369      * The result is the match object representing the intersection of the set
370      * of packets described by this match with the set of packets described by
371      * the filter match. If the intersection of the two sets is empty, the
372      * return match will be null.
373      *
374      * @param filter
375      *            the match with which attempting the merge
376      * @return a new Match object describing the set of packets represented by
377      *         the intersection of this and the filter matches. null if the
378      *         intersection is empty.
379      */
380     public Match mergeWithFilter(Match filter) {
381         return this.getIntersection(filter);
382     }
383
384     /**
385      * Return the match representing the intersection of the set of packets
386      * described by this match with the set of packets described by the other
387      * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
388      * m.getIntersection(o) == o where u is an empty match (universal set, all
389      * packets) and o is the null match (empty set).
390      *
391      * @param other
392      *            the match with which computing the intersection
393      * @return a new Match object representing the intersection of the set of
394      *         packets described by this match with the set of packets described
395      *         by the other match. null when the intersection is the empty set.
396      */
397     public Match getIntersection(Match other) {
398         // If no intersection, return the empty set
399         if (!this.intersetcs(other)) {
400             return null;
401         }
402         // Check if any of the two is the universal match
403         if (this.getMatches() == 0) {
404             return other.clone();
405         }
406         if (other.getMatches() == 0) {
407             return this.clone();
408         }
409         // Derive the intersection
410         Match intersection = new Match();
411         for (MatchType type : MatchType.values()) {
412             if (this.isAny(type) && other.isAny(type)) {
413                 continue;
414             }
415             if (this.isAny(type)) {
416                 intersection.setField(other.getField(type).clone());
417                 continue;
418             } else if (other.isAny(type)) {
419                 intersection.setField(this.getField(type).clone());
420                 continue;
421             }
422             // Either they are equal or it is about IP address
423             switch (type) {
424             // When it is about IP address, take the wider prefix address
425             // between the twos
426             case NW_SRC:
427             case NW_DST:
428                 MatchField thisField = this.getField(type);
429                 MatchField otherField = other.getField(type);
430                 InetAddress thisAddress = (InetAddress) thisField.getValue();
431                 InetAddress otherAddress = (InetAddress) otherField.getValue();
432                 InetAddress thisMask = (InetAddress) thisField.getMask();
433                 InetAddress otherMask = (InetAddress) otherField.getMask();
434
435                 int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
436                         .getSubnetMaskLength(thisMask);
437                 int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
438                         .getSubnetMaskLength(otherMask);
439                 if (thisMaskLen < otherMaskLen) {
440                     intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(otherAddress, otherMaskLen),
441                             otherMask));
442                 } else {
443                     intersection.setField(new MatchField(type, NetUtils.getSubnetPrefix(thisAddress, thisMaskLen),
444                             thisMask));
445                 }
446                 break;
447             default:
448                 // this and other match field are equal for this type, pick this
449                 // match field
450                 intersection.setField(this.getField(type).clone());
451             }
452         }
453         return intersection;
454     }
455
456     /**
457      * Checks whether the intersection of the set of packets described by this
458      * match with the set of packets described by the other match is non empty
459      *
460      * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
461      *
462      * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
463      *
464      * then their respective matching packets set intersection is non empty:
465      * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
466      *
467      * @param other
468      *            the other match with which testing the intersection
469      * @return true if the intersection of the respective matching packets sets
470      *         is non empty
471      */
472     public boolean intersetcs(Match other) {
473         // No intersection with the empty set
474         if (other == null) {
475             return false;
476         }
477         // Always intersection with the universal set
478         if (this.getMatches() == 0 || other.getMatches() == 0) {
479             return true;
480         }
481         // Iterate through the MatchType defined in the filter
482         for (MatchType type : MatchType.values()) {
483             if (this.isAny(type) || other.isAny(type)) {
484                 continue;
485             }
486
487             MatchField thisField = this.getField(type);
488             MatchField otherField = other.getField(type);
489
490             switch (type) {
491             case DL_SRC:
492             case DL_DST:
493                 if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
494                     return false;
495                 }
496                 break;
497             case NW_SRC:
498             case NW_DST:
499                 InetAddress thisAddress = (InetAddress) thisField.getValue();
500                 InetAddress otherAddress = (InetAddress) otherField.getValue();
501                 // Validity check
502                 if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
503                         || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
504                     return false;
505                 }
506                 InetAddress thisMask = (InetAddress) thisField.getMask();
507                 InetAddress otherMask = (InetAddress) otherField.getMask();
508                 if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
509                         && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
510                     return false;
511                 }
512                 break;
513             default:
514                 if (!thisField.getValue().equals(otherField.getValue())) {
515                     return false;
516                 }
517             }
518         }
519         return true;
520     }
521
522     @Override
523     public int hashCode() {
524         final int prime = 31;
525         int result = 1;
526         if (this.fields == null) {
527             result = prime * result;
528         } else {
529             // use a tree map as the order of hashMap is not guaranteed.
530             // 2 Match objects with fields in different order are still equal.
531             // Hence the hashCode should be the same too.
532             TreeMap<MatchType, MatchField> tm = new TreeMap<MatchType, MatchField>(this.fields);
533             for (MatchType field : tm.keySet()) {
534                 MatchField f = tm.get(field);
535                 int fieldHashCode = (field==null ? 0 : field.calculateConsistentHashCode()) ^
536                              (f==null ? 0 : f.hashCode());
537                 result = prime * result + fieldHashCode;
538             }
539         }
540         result = prime * result + matches;
541         return result;
542     }
543
544     @Override
545     public boolean equals(Object obj) {
546         if (this == obj) {
547             return true;
548         }
549         if (obj == null) {
550             return false;
551         }
552         if (getClass() != obj.getClass()) {
553             return false;
554         }
555         Match other = (Match) obj;
556         if (fields == null) {
557             if (other.fields != null) {
558                 return false;
559             }
560         } else if (!fields.equals(other.fields)) {
561             return false;
562         }
563         if (matches != other.matches) {
564             return false;
565         }
566         return true;
567     }
568
569     @Override
570     public String toString() {
571         StringBuilder builder = new StringBuilder();
572         builder.append("Match [fields=");
573         builder.append(fields);
574         builder.append(", matches=");
575         builder.append(matches);
576         builder.append("]");
577         return builder.toString();
578     }
579
580 }