ISSUE
[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.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, Serializable {
43         private static final long serialVersionUID = 1L;
44         private static final Map<MatchType, MatchType> reversableMatches;
45     static {
46         Map<MatchType, MatchType> map = new HashMap<MatchType, MatchType>();
47         map.put(MatchType.DL_SRC, MatchType.DL_DST);
48         map.put(MatchType.DL_DST, MatchType.DL_SRC);
49         map.put(MatchType.NW_SRC, MatchType.NW_DST);
50         map.put(MatchType.NW_DST, MatchType.NW_SRC);
51         map.put(MatchType.TP_SRC, MatchType.TP_DST);
52         map.put(MatchType.TP_DST, MatchType.TP_SRC);
53         reversableMatches = Collections.unmodifiableMap(map);
54     }
55     private Map<MatchType, MatchField> fields;
56     private int matches; // concise way to tell which fields the match is set for (may remove if not needed)
57
58     public Match() {
59         fields = new HashMap<MatchType, MatchField>();
60         matches = 0;
61     }
62
63     public Match(Match match) {
64         fields = new HashMap<MatchType, MatchField>(match.fields);
65         matches = match.matches;
66     }
67
68     /**
69      * Generic setter for frame/packet/message's header fields against which to match
70      * Note: For MAC addresses, please pass the cloned value to this function
71      *
72      * @param type              packet's header field type
73      * @param value     field's value to assign to the match
74      * @param mask              field's bitmask to apply to the match (has to be of the same class type of value)
75      */
76     public void setField(MatchType type, Object value, Object mask) {
77         MatchField field = new MatchField(type, value, mask);
78         if (field.isValid()) {
79             fields.put(type, field);
80             matches |= type.getIndex();
81         }
82     }
83
84     /**
85      * Generic setter for frame/packet/message's header fields against which to match
86      * Note: For MAC addresses, please pass the cloned value to this function
87      *
88      * @param type              packet's header field type
89      * @param value     field's value to assign to the match
90      */
91     public void setField(MatchType type, Object value) {
92         MatchField field = new MatchField(type, value);
93         if (field.isValid()) {
94             fields.put(type, field);
95             matches |= type.getIndex();
96         }
97     }
98
99     /**
100      * Generic setter for frame/packet/message's header field against which to match
101      *
102      * @param field the fields parameters as MAtchField object
103      */
104     public void setField(MatchField field) {
105         if (field.isValid()) {
106             fields.put(field.getType(), field);
107             matches |= field.getType().getIndex();
108         }
109     }
110
111     /**
112      * Generic method to clear a field from the match
113      */
114     public void clearField(MatchType type) {
115         fields.remove(type);
116         matches &= ~type.getIndex();
117     }
118
119     /**
120      * Generic getter for fields against which the match is programmed
121      *
122      * @param type      frame/packet/message's header field type
123      * @return
124      */
125     public MatchField getField(MatchType type) {
126         return fields.get(type);
127     }
128
129     /**
130      * Returns the fields the match is set for in a bitmask fashion
131      * Each bit represents a field the match is configured for
132      *
133      * @return the 32 bit long mask (Refer to {@code}org.opendaylight.controller.sal.match.MatchElement)
134      */
135     public int getMatches() {
136         return matches;
137     }
138
139     /**
140      * Returns the list of MatchType fields the match is set for
141      *
142      * @return List of individual MatchType fields. 
143      */
144     public List<MatchType> getMatchesList() {
145         return new ArrayList<MatchType>(fields.keySet());
146     }
147
148     /**
149      * Returns the list of MatchFields the match is set for
150      *
151      * @return List of individual MatchField values. 
152      */
153     @XmlElement(name="matchField")
154     public List<MatchField> getMatchFields() {
155         return new ArrayList<MatchField>(fields.values());
156     }
157     
158     /**
159      * Returns whether this match is for an IPv6 flow
160      */
161     public boolean isIPv6() {
162         return (isPresent(MatchType.DL_TYPE)
163                 && ((Short) getField(MatchType.DL_TYPE).getValue())
164                         .equals(EtherTypes.IPv6.shortValue())
165                 || isPresent(MatchType.NW_PROTO)
166                 && ((Byte) getField(MatchType.NW_PROTO).getValue())
167                         .equals(IPProtocols.IPV6ICMP.byteValue())
168                 || isPresent(MatchType.NW_SRC)
169                 && getField(MatchType.NW_SRC).getValue() instanceof Inet6Address || isPresent(MatchType.NW_DST)
170                 && getField(MatchType.NW_DST).getValue() instanceof Inet6Address);
171     }
172
173     /**
174      * Returns whether this match is for an IPv4 flow
175      */
176     public boolean isIPv4() {
177         return !isIPv6();
178     }
179
180     /**
181      * Returns whether for the specified field type the match is to be considered "any"
182      * Equivalent to say this match does not care about the value of the specified field
183      *
184      * @param type
185      * @return
186      */
187     public boolean isAny(MatchType type) {
188         //return ((fields.get(type) == null) || (fields.get(type).getBitMask() == 0L));
189         return !fields.containsKey(type);
190     }
191
192     /**
193      * Returns whether a match for the specified field type is configured
194      *
195      * @param type
196      * @return
197      */
198     public boolean isPresent(MatchType type) {
199         return (fields.get(type) != null);
200     }
201
202     @Override
203     public Match clone() {
204         Match cloned = null;
205         try {
206             cloned = (Match) super.clone();
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
231                 .entrySet()) {
232             MatchType from = entry.getKey();
233             MatchType to = entry.getValue();
234             if (this.isPresent(from)) {
235                 reverse.setField(to, this.getField(from).getValue(), this
236                         .getField(from).getMask());
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 compatible
250      *
251      * For example, Let's suppose the filter has the following MatchFields:
252      * DL_TYPE = 0x800
253      * NW_DST =  172.20.30.110/24
254      *
255      * while this match has the following MatchFields:
256      * NW_DST = 172.20.30.45/24
257      * TP_DST = 80
258      *
259      * Then the function would return false as the two Match are not conflicting
260      *
261      * Note: the mask value is taken into account only for MatchType.NW_SRC and MatchType.NW_DST
262      *
263      * @param match the MAtch describing the filter
264      * @return true if the match is conflicting with the filter, false otherwise
265      */
266     public boolean conflictWithFilter(Match filter) {
267         // Iterate through the MatchType defined in the filter
268         for (MatchType type : filter.getMatchesList()) {
269             if (this.isAny(type)) {
270                 continue;
271             }
272
273             MatchField thisField = this.getField(type);
274             MatchField filterField = filter.getField(type);
275
276             switch (type) {
277             case DL_SRC:
278             case DL_DST:
279                 if (Arrays.equals((byte[]) thisField.getValue(),
280                         (byte[]) filterField.getValue())) {
281                     return false;
282                 }
283                 break;
284             case NW_SRC:
285             case NW_DST:
286                 InetAddress thisAddress = (InetAddress) thisField.getValue();
287                 InetAddress filterAddress = (InetAddress) filterField
288                         .getValue();
289                 // Validity check
290                 if (thisAddress instanceof Inet4Address
291                         && filterAddress instanceof Inet6Address
292                         || thisAddress instanceof Inet6Address
293                         && filterAddress instanceof Inet4Address) {
294                     return true;
295                 }
296                 InetAddress thisMask = (InetAddress) filter.getField(type)
297                         .getMask();
298                 InetAddress filterMask = (InetAddress) filter.getField(type)
299                         .getMask();
300                 // thisAddress has to be in same subnet of filterAddress
301                 if (NetUtils.inetAddressConflict(thisAddress, filterAddress,
302                         thisMask, filterMask)) {
303                     return true;
304                 }
305                 break;
306             default:
307                 if (!thisField.getValue().equals(filterField.getValue())) {
308                     return true;
309                 }
310             }
311             //TODO: check v4 v6 incompatibility
312         }
313         return false;
314     }
315
316     /**
317      * Merge the current Match fields with the fields of the filter Match
318      * A check is first run to see if this Match is compatible with the
319      * filter Match. If it is not, the merge is not attempted.
320      *
321      *
322      * @param filter
323      * @return
324      */
325     public Match mergeWithFilter(Match filter) {
326         if (!this.conflictWithFilter(filter)) {
327             /*
328              * No conflict with the filter
329              * We can copy over the fields which this match does not have
330              */
331             for (MatchType type : filter.getMatchesList()) {
332                 if (this.isAny(type)) {
333                     this.setField(filter.getField(type).clone());
334                 }
335             }
336         }
337         return this;
338     }
339
340     @Override
341     public int hashCode() {
342         return HashCodeBuilder.reflectionHashCode(this);
343     }
344
345     @Override
346     public boolean equals(Object obj) {
347         return EqualsBuilder.reflectionEquals(this, obj);
348     }
349
350     @Override
351     public String toString() {
352         return "Match[" + fields.values() + "]";
353     }
354 }