Initial opendaylight infrastructure commit!!
[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.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.Collections;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Map.Entry;
22
23 import javax.xml.bind.annotation.XmlAccessType;
24 import javax.xml.bind.annotation.XmlAccessorType;
25 import javax.xml.bind.annotation.XmlElement;
26 import javax.xml.bind.annotation.XmlRootElement;
27
28 import org.apache.commons.lang3.builder.EqualsBuilder;
29 import org.apache.commons.lang3.builder.HashCodeBuilder;
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 {
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     @XmlElement
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 whether this match is for an IPv6 flow
149      */
150     public boolean isIPv6() {
151         return (isPresent(MatchType.DL_TYPE)
152                 && ((Short) getField(MatchType.DL_TYPE).getValue())
153                         .equals(EtherTypes.IPv6.shortValue())
154                 || isPresent(MatchType.NW_PROTO)
155                 && ((Byte) getField(MatchType.NW_PROTO).getValue())
156                         .equals(IPProtocols.IPV6ICMP.byteValue())
157                 || isPresent(MatchType.NW_SRC)
158                 && getField(MatchType.NW_SRC).getValue() instanceof Inet6Address || isPresent(MatchType.NW_DST)
159                 && getField(MatchType.NW_DST).getValue() instanceof Inet6Address);
160     }
161
162     /**
163      * Returns whether this match is for an IPv4 flow
164      */
165     public boolean isIPv4() {
166         return !isIPv6();
167     }
168
169     /**
170      * Returns whether for the specified field type the match is to be considered "any"
171      * Equivalent to say this match does not care about the value of the specified field
172      *
173      * @param type
174      * @return
175      */
176     public boolean isAny(MatchType type) {
177         //return ((fields.get(type) == null) || (fields.get(type).getBitMask() == 0L));
178         return !fields.containsKey(type);
179     }
180
181     /**
182      * Returns whether a match for the specified field type is configured
183      *
184      * @param type
185      * @return
186      */
187     public boolean isPresent(MatchType type) {
188         return (fields.get(type) != null);
189     }
190
191     @Override
192     public Match clone() {
193         Match cloned = null;
194         try {
195             cloned = (Match) super.clone();
196             cloned.fields = new HashMap<MatchType, MatchField>();
197             for (Entry<MatchType, MatchField> entry : this.fields.entrySet()) {
198                 cloned.fields.put(entry.getKey(), entry.getValue().clone());
199             }
200         } catch (CloneNotSupportedException e) {
201             throw new RuntimeException(e);
202         }
203         return cloned;
204     }
205
206     /**
207      * Returns a reversed version of this match
208      * For example, in the reversed version the network source and destination
209      * addresses will be exchanged. Non symmetric match field will not be
210      * copied over into the reversed match version, like input port.
211      *
212      * @return
213      */
214     public Match reverse() {
215         // Copy over all fields
216         Match reverse = this.clone();
217
218         // Flip symmetric fields
219         for (Map.Entry<MatchType, MatchType> entry : Match.reversableMatches
220                 .entrySet()) {
221             MatchType from = entry.getKey();
222             MatchType to = entry.getValue();
223             if (this.isPresent(from)) {
224                 reverse.setField(to, this.getField(from).getValue(), this
225                         .getField(from).getMask());
226             }
227         }
228
229         // Reset asymmetric fields
230         reverse.clearField(MatchType.IN_PORT);
231
232         return reverse;
233     }
234
235     /**
236      * Check whether the current match conflicts with the passed filter match
237      * This match conflicts with the filter if for at least a MatchType defined
238      * in the filter match, the respective MatchFields differ or are not compatible
239      *
240      * For example, Let's suppose the filter has the following MatchFields:
241      * DL_TYPE = 0x800
242      * NW_DST =  172.20.30.110/24
243      *
244      * while this match has the following MatchFields:
245      * NW_DST = 172.20.30.45/24
246      * TP_DST = 80
247      *
248      * Then the function would return false as the two Match are not conflicting
249      *
250      * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST
251      *
252      * @param match the MAtch describing the filter
253      * @return true if the match is conflicting with the filter, false otherwise
254      */
255     public boolean conflictWithFilter(Match filter) {
256         // Iterate through the MatchType defined in the filter
257         for (MatchType type : filter.getMatchesList()) {
258             if (this.isAny(type)) {
259                 continue;
260             }
261
262             MatchField thisField = this.getField(type);
263             MatchField filterField = filter.getField(type);
264
265             switch (type) {
266             case DL_SRC:
267             case DL_DST:
268                 if (Arrays.equals((byte[]) thisField.getValue(),
269                         (byte[]) filterField.getValue())) {
270                     return false;
271                 }
272                 break;
273             case NW_SRC:
274             case NW_DST:
275                 InetAddress thisAddress = (InetAddress) thisField.getValue();
276                 InetAddress filterAddress = (InetAddress) filterField
277                         .getValue();
278                 // Validity check
279                 if (thisAddress instanceof Inet4Address
280                         && filterAddress instanceof Inet6Address
281                         || thisAddress instanceof Inet6Address
282                         && filterAddress instanceof Inet4Address) {
283                     return true;
284                 }
285                 InetAddress thisMask = (InetAddress) filter.getField(type)
286                         .getMask();
287                 InetAddress filterMask = (InetAddress) filter.getField(type)
288                         .getMask();
289                 // thisAddress has to be in same subnet of filterAddress
290                 if (NetUtils.inetAddressConflict(thisAddress, filterAddress,
291                         thisMask, filterMask)) {
292                     return true;
293                 }
294                 break;
295             default:
296                 if (!thisField.getValue().equals(filterField.getValue())) {
297                     return true;
298                 }
299             }
300             //TODO: check v4 v6 incompatibility
301         }
302         return false;
303     }
304
305     /**
306      * Merge the current Match fields with the fields of the filter Match
307      * A check is first run to see if this Match is compatible with the
308      * filter Match. If it is not, the merge is not attempted.
309      *
310      *
311      * @param filter
312      * @return
313      */
314     public Match mergeWithFilter(Match filter) {
315         if (!this.conflictWithFilter(filter)) {
316             /*
317              * No conflict with the filter
318              * We can copy over the fields which this match does not have
319              */
320             for (MatchType type : filter.getMatchesList()) {
321                 if (this.isAny(type)) {
322                     this.setField(filter.getField(type).clone());
323                 }
324             }
325         }
326         return this;
327     }
328
329     @Override
330     public int hashCode() {
331         return HashCodeBuilder.reflectionHashCode(this);
332     }
333
334     @Override
335     public boolean equals(Object obj) {
336         return EqualsBuilder.reflectionEquals(this, obj);
337     }
338
339     @Override
340     public String toString() {
341         return "Match[" + fields.values() + "]";
342     }
343 }