Merge "Move adsal into its own subdirectory."
[controller.git] / opendaylight / adsal / sal / api / src / main / java / org / opendaylight / controller / sal / match / extensible / 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.extensible;
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.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
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
31 import org.opendaylight.controller.sal.utils.NetUtils;
32
33 /**
34  * Represents the generic match criteria for a network frame/packet/message
35  * It contains a collection of individual field match
36  *
37  */
38 @XmlRootElement
39 @XmlAccessorType(XmlAccessType.NONE)
40 public class Match implements Cloneable, Serializable {
41         private static final long serialVersionUID = 1L;
42     private Map<String, MatchField<?>> fields;
43
44     public Match() {
45         fields = new HashMap<String, MatchField<?>>();
46     }
47
48     public Match(Match match) {
49         fields = new HashMap<String, MatchField<?>>(match.fields);
50     }
51
52     /**
53      * Generic setter for frame/packet/message's header field against which to match
54      *
55      * @param field the fields parameters as MAtchField object
56      */
57     public void setField(MatchField<?> field) {
58         if (field.isValid()) {
59             fields.put(field.getType(), field);
60         }
61     }
62
63     /**
64      * Generic method to clear a field from the match
65      */
66     public void clearField(String type) {
67         fields.remove(type);
68     }
69
70     /**
71      * Generic getter for fields against which the match is programmed
72      *
73      * @param type  frame/packet/message's header field type
74      * @return
75      */
76     public MatchField<?> getField(String type) {
77         return fields.get(type);
78     }
79
80     /**
81      * Returns the list of MatchType fields the match is set for
82      *
83      * @return List of individual MatchType fields.
84      */
85     public List<String> getMatchesList() {
86         return new ArrayList<String>(fields.keySet());
87     }
88
89     /**
90      * Returns the list of MatchFields the match is set for
91      *
92      * @return List of individual MatchField values.
93      */
94     @XmlElement(name="matchField")
95     public List<MatchField<?>> getMatchFields() {
96         return new ArrayList<MatchField<?>>(fields.values());
97     }
98
99     /**
100      * Returns whether this match is for an IPv6 flow
101      */
102     public boolean isIPv6() {
103         if (isPresent(DlType.TYPE)) {
104             for (MatchField<?> field : fields.values()) {
105                 if (!field.isV6()) {
106                     return false;
107                 }
108             }
109         }
110         return true;
111     }
112
113     /**
114      * Returns whether this match is for an IPv4 flow
115      */
116     public boolean isIPv4() {
117         return !isIPv6();
118     }
119
120     /**
121      * Returns whether for the specified field type the match is to be considered "any"
122      * Equivalent to say this match does not care about the value of the specified field
123      *
124      * @param type
125      * @return
126      */
127     public boolean isAny(String type) {
128         return !fields.containsKey(type);
129     }
130
131     /**
132      * Returns whether a match for the specified field type is configured
133      *
134      * @param type
135      * @return
136      */
137     public boolean isPresent(String type) {
138         return (fields.get(type) != null);
139     }
140
141     public boolean isEmpty() {
142         return fields.isEmpty();
143     }
144
145     @Override
146     public Match clone() {
147         Match cloned = null;
148         try {
149             cloned = (Match) super.clone();
150             cloned.fields = new HashMap<String, MatchField<?>>();
151             for (Entry<String, MatchField<?>> entry : this.fields.entrySet()) {
152                 cloned.fields.put(entry.getKey(), entry.getValue().clone());
153             }
154         } catch (CloneNotSupportedException e) {
155             throw new RuntimeException(e);
156         }
157         return cloned;
158     }
159
160     /**
161      * Returns a reversed version of this match
162      * For example, in the reversed version the network source and destination
163      * addresses will be exchanged. Non symmetric match field will not be
164      * copied over into the reversed match version, like input port.
165      *
166      * @return
167      */
168     public Match reverse() {
169         Match reverse = new Match();
170         for (MatchField<?> field : fields.values()) {
171             reverse.setField(field.hasReverse()? field.getReverse() : field.clone());
172         }
173
174         // Reset asymmetric fields
175         reverse.clearField(InPort.TYPE);
176
177         return reverse;
178     }
179
180     /**
181      * Check whether the current match conflicts with the passed filter match
182      * This match conflicts with the filter if for at least a MatchType defined
183      * in the filter match, the respective MatchFields differ or are not
184      * compatible
185      *
186      * In other words the function returns true if the set of packets described
187      * by one match and the set of packets described by the other match are
188      * disjoint. Equivalently, if the intersection of the two sets of packets
189      * described by the two org.opendaylight.controller.sal.matches is an empty.
190      *
191      * For example, Let's suppose the filter has the following MatchFields:
192      * DL_TYPE = 0x800
193      * NW_DST = 172.20.30.110/24
194      *
195      * while this match has the following MatchFields:
196      * DL_TYPE = 0x800
197      * NW_DST = 172.20.30.45/24
198      * TP_DST = 80
199      *
200      * Then the function would return false as the two Match are not
201      * conflicting.
202      *
203      * Note: the mask value is taken into account only for MatchType.NW_SRC and
204      * MatchType.NW_DST
205      *
206      * @param match
207      *            the Match describing the filter
208      * @return true if the set of packets described by one match and the set of
209      *         packets described by the other match are disjoint, false
210      *         otherwise
211      */
212     public boolean conflictWithFilter(Match filter) {
213         return !this.intersetcs(filter);
214     }
215
216     /**
217      * Merge the current Match fields with the fields of the filter Match. A
218      * check is first run to see if this Match is compatible with the filter
219      * Match. If it is not, the merge is not attempted.
220      *
221      * The result is the match object representing the intersection of the set
222      * of packets described by this match with the set of packets described by
223      * the filter match. If the intersection of the two sets is empty, the
224      * return match will be null.
225      *
226      * @param filter
227      *            the match with which attempting the merge
228      * @return a new Match object describing the set of packets represented by
229      *         the intersection of this and the filter org.opendaylight.controller.sal.matches. null if the
230      *         intersection is empty.
231      */
232     public Match mergeWithFilter(Match filter) {
233         return this.getIntersection(filter);
234     }
235
236     /**
237      * Return the match representing the intersection of the set of packets
238      * described by this match with the set of packets described by the other
239      * match. Such as m.getIntersection(m) == m, m.getIntersection(u) == m and
240      * m.getIntersection(o) == o where u is an empty match (universal set, all
241      * packets) and o is the null match (empty set).
242      *
243      * @param other
244      *            the match with which computing the intersection
245      * @return a new Match object representing the intersection of the set of
246      *         packets described by this match with the set of packets described
247      *         by the other match. null when the intersection is the empty set.
248      */
249     public Match getIntersection(Match other) {
250         // If no intersection, return the empty set
251         if (!this.intersetcs(other)) {
252             return null;
253         }
254         // Check if any of the two is the universal match
255         if (this.isEmpty()) {
256             return other.clone();
257         }
258         if (other.isEmpty()) {
259             return this.clone();
260         }
261         // Get all the match types for both filters
262         Set<String> allTypes = new HashSet<String>(this.fields.keySet());
263         allTypes.addAll(new HashSet<String>(other.fields.keySet()));
264         // Derive the intersection
265         Match intersection = new Match();
266         for (String type : allTypes) {
267             if (this.isAny(type) && other.isAny(type)) {
268                 continue;
269             }
270             if (this.isAny(type)) {
271                 intersection.setField(other.getField(type).clone());
272                 continue;
273             } else if (other.isAny(type)) {
274                 intersection.setField(this.getField(type).clone());
275                 continue;
276             }
277             // Either they are equal or it is about IP address
278             switch (type) {
279             // When it is about IP address, take the wider prefix address
280             // between the twos
281             case NwSrc.TYPE:
282             case NwDst.TYPE:
283                 MatchField<?> thisField = this.getField(type);
284                 MatchField<?> otherField = other.getField(type);
285                 InetAddress thisAddress = (InetAddress) thisField.getValue();
286                 InetAddress otherAddress = (InetAddress) otherField.getValue();
287                 InetAddress thisMask = (InetAddress) thisField.getMask();
288                 InetAddress otherMask = (InetAddress) otherField.getMask();
289
290                 int thisMaskLen = (thisMask == null) ? ((thisAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
291                         .getSubnetMaskLength(thisMask);
292                 int otherMaskLen = (otherMask == null) ? ((otherAddress instanceof Inet4Address) ? 32 : 128) : NetUtils
293                         .getSubnetMaskLength(otherMask);
294
295                 InetAddress subnetPrefix = null;
296                 InetAddress subnetMask = null;
297                 if (thisMaskLen < otherMaskLen) {
298                     subnetPrefix = NetUtils.getSubnetPrefix(otherAddress, otherMaskLen);
299                     subnetMask = otherMask;
300                 } else {
301                     subnetPrefix = NetUtils.getSubnetPrefix(thisAddress, thisMaskLen);
302                     subnetMask = thisMask;
303                 }
304                 MatchField<?> field = (type.equals(NwSrc.TYPE)) ? new NwSrc(subnetPrefix, subnetMask) : new NwDst(
305                         subnetPrefix, subnetMask);
306                 intersection.setField(field);
307                 break;
308             default:
309                 // this and other match field are equal for this type, pick this
310                 // match field
311                 intersection.setField(this.getField(type).clone());
312             }
313         }
314         return intersection;
315     }
316
317     /**
318      * Checks whether the intersection of the set of packets described by this
319      * match with the set of packets described by the other match is non empty
320      *
321      * For example, if this match is: DL_SRC = 00:cc:bb:aa:11:22
322      *
323      * and the other match is: DL_TYPE = 0x800 NW_SRC = 1.2.3.4
324      *
325      * then their respective matching packets set intersection is non empty:
326      * DL_SRC = 00:cc:bb:aa:11:22 DL_TYPE = 0x800 NW_SRC = 1.2.3.4
327      *
328      * @param other
329      *            the other match with which testing the intersection
330      * @return true if the intersection of the respective matching packets sets
331      *         is non empty
332      */
333     public boolean intersetcs(Match other) {
334         // No intersection with the empty set
335         if (other == null) {
336             return false;
337         }
338         // Always intersection with the universal set
339         if (this.isEmpty() || other.isEmpty()) {
340             return true;
341         }
342
343         // Get all the match types for both filters
344         Set<String> allTypes = new HashSet<String>(this.fields.keySet());
345         allTypes.addAll(new HashSet<String>(other.fields.keySet()));
346
347         // Iterate through all the match types defined in the two filters
348         for (String type : allTypes) {
349             if (this.isAny(type) || other.isAny(type)) {
350                 continue;
351             }
352
353             MatchField<?> thisField = this.getField(type);
354             MatchField<?> otherField = other.getField(type);
355
356             switch (type) {
357             case DlSrc.TYPE:
358             case DlDst.TYPE:
359                 if (!Arrays.equals((byte[]) thisField.getValue(), (byte[]) otherField.getValue())) {
360                     return false;
361                 }
362                 break;
363             case NwSrc.TYPE:
364             case NwDst.TYPE:
365                 InetAddress thisAddress = (InetAddress) thisField.getValue();
366                 InetAddress otherAddress = (InetAddress) otherField.getValue();
367                 // Validity check
368                 if (thisAddress instanceof Inet4Address && otherAddress instanceof Inet6Address
369                         || thisAddress instanceof Inet6Address && otherAddress instanceof Inet4Address) {
370                     return false;
371                 }
372                 InetAddress thisMask = (InetAddress) thisField.getMask();
373                 InetAddress otherMask = (InetAddress) otherField.getMask();
374                 if (NetUtils.inetAddressConflict(thisAddress, otherAddress, thisMask, otherMask)
375                         && NetUtils.inetAddressConflict(otherAddress, thisAddress, otherMask, thisMask)) {
376                     return false;
377                 }
378                 break;
379             default:
380                 if (!thisField.getValue().equals(otherField.getValue())) {
381                     return false;
382                 }
383             }
384         }
385         return true;
386     }
387
388     @Override
389     public int hashCode() {
390         final int prime = 31;
391         int result = 1;
392         result = prime * result + ((fields == null) ? 0 : fields.hashCode());
393         return result;
394     }
395
396     @Override
397     public boolean equals(Object obj) {
398         if (this == obj) {
399             return true;
400         }
401         if (obj == null) {
402             return false;
403         }
404         if (!(obj instanceof Match)) {
405             return false;
406         }
407         Match other = (Match) obj;
408         if (fields == null) {
409             if (other.fields != null) {
410                 return false;
411             }
412         } else if (!fields.equals(other.fields)) {
413             return false;
414         }
415         return true;
416     }
417
418     @Override
419     public String toString() {
420         return "Match[" + fields.values() + "]";
421     }
422 }