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