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