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