OpenDaylight Controller functional modules.
[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.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
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.apache.commons.lang3.builder.EqualsBuilder;
30 import org.apache.commons.lang3.builder.HashCodeBuilder;
31 import org.opendaylight.controller.sal.utils.EtherTypes;
32 import org.opendaylight.controller.sal.utils.IPProtocols;
33 import org.opendaylight.controller.sal.utils.NetUtils;
34
35 /**
36  * Represents the generic match criteria for a network frame/packet/message
37  * It contains a collection of individual field match
38  *
39  */
40 @XmlRootElement
41 @XmlAccessorType(XmlAccessType.NONE)
42 public class Match implements Cloneable {
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.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
230                 .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
235                         .getField(from).getMask());
236             }
237         }
238
239         // Reset asymmetric fields
240         reverse.clearField(MatchType.IN_PORT);
241
242         return reverse;
243     }
244
245     /**
246      * Check whether the current match conflicts with the passed filter match
247      * This match conflicts with the filter if for at least a MatchType defined
248      * in the filter match, the respective MatchFields differ or are not compatible
249      *
250      * For example, Let's suppose the filter has the following MatchFields:
251      * DL_TYPE = 0x800
252      * NW_DST =  172.20.30.110/24
253      *
254      * while this match has the following MatchFields:
255      * NW_DST = 172.20.30.45/24
256      * TP_DST = 80
257      *
258      * Then the function would return false as the two Match are not conflicting
259      *
260      * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST
261      *
262      * @param match the MAtch describing the filter
263      * @return true if the match is conflicting with the filter, false otherwise
264      */
265     public boolean conflictWithFilter(Match filter) {
266         // Iterate through the MatchType defined in the filter
267         for (MatchType type : filter.getMatchesList()) {
268             if (this.isAny(type)) {
269                 continue;
270             }
271
272             MatchField thisField = this.getField(type);
273             MatchField filterField = filter.getField(type);
274
275             switch (type) {
276             case DL_SRC:
277             case DL_DST:
278                 if (Arrays.equals((byte[]) thisField.getValue(),
279                         (byte[]) filterField.getValue())) {
280                     return false;
281                 }
282                 break;
283             case NW_SRC:
284             case NW_DST:
285                 InetAddress thisAddress = (InetAddress) thisField.getValue();
286                 InetAddress filterAddress = (InetAddress) filterField
287                         .getValue();
288                 // Validity check
289                 if (thisAddress instanceof Inet4Address
290                         && filterAddress instanceof Inet6Address
291                         || thisAddress instanceof Inet6Address
292                         && filterAddress instanceof Inet4Address) {
293                     return true;
294                 }
295                 InetAddress thisMask = (InetAddress) filter.getField(type)
296                         .getMask();
297                 InetAddress filterMask = (InetAddress) filter.getField(type)
298                         .getMask();
299                 // thisAddress has to be in same subnet of filterAddress
300                 if (NetUtils.inetAddressConflict(thisAddress, filterAddress,
301                         thisMask, filterMask)) {
302                     return true;
303                 }
304                 break;
305             default:
306                 if (!thisField.getValue().equals(filterField.getValue())) {
307                     return true;
308                 }
309             }
310             //TODO: check v4 v6 incompatibility
311         }
312         return false;
313     }
314
315     /**
316      * Merge the current Match fields with the fields of the filter Match
317      * A check is first run to see if this Match is compatible with the
318      * filter Match. If it is not, the merge is not attempted.
319      *
320      *
321      * @param filter
322      * @return
323      */
324     public Match mergeWithFilter(Match filter) {
325         if (!this.conflictWithFilter(filter)) {
326             /*
327              * No conflict with the filter
328              * We can copy over the fields which this match does not have
329              */
330             for (MatchType type : filter.getMatchesList()) {
331                 if (this.isAny(type)) {
332                     this.setField(filter.getField(type).clone());
333                 }
334             }
335         }
336         return this;
337     }
338
339     @Override
340     public int hashCode() {
341         return HashCodeBuilder.reflectionHashCode(this);
342     }
343
344     @Override
345     public boolean equals(Object obj) {
346         return EqualsBuilder.reflectionEquals(this, obj);
347     }
348
349     @Override
350     public String toString() {
351         return "Match[" + fields.values() + "]";
352     }
353 }