Bug 2951 - ipv6 addresses containing zeros in input flow should be handled in operati...
[openflowplugin.git] / applications / statistics-manager / src / main / java / org / opendaylight / openflowplugin / applications / statistics / manager / impl / helper / MatchComparatorHelper.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.applications.statistics.manager.impl.helper;
9
10 import com.google.common.net.InetAddresses;
11 import java.net.Inet4Address;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
16 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match;
22
23 /**
24  * @author joe
25  *
26  */
27 public class MatchComparatorHelper {
28
29     private static final int DEFAULT_SUBNET = 32;
30     private static final int IPV4_MASK_LENGTH = 32;
31     private static final int SHIFT_OCTET_1 = 24;
32     private static final int SHIFT_OCTET_2 = 16;
33     private static final int SHIFT_OCTET_3 = 8;
34     private static final int SHIFT_OCTET_4 = 0;
35     private static final int POSITION_OCTET_1 = 0;
36     private static final int POSITION_OCTET_2 = 1;
37     private static final int POSITION_OCTET_3 = 2;
38     private static final int POSITION_OCTET_4 = 3;
39
40     /*
41      * Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
42      * (upper or lower or mix). Ethernet Match which controller receives from switch is always an upper case string.
43      * Default EthernetMatch equals doesn't use equalsIgnoreCase() and hence it fails. E.g User provided mac address
44      * string in flow match is aa:bb:cc:dd:ee:ff and when controller fetch statistic data, openflow driver library
45      * returns AA:BB:CC:DD:EE:FF and default eqauls fails here.
46      */
47     @VisibleForTesting
48     static boolean ethernetMatchEquals(final EthernetMatch statsEthernetMatch, final EthernetMatch storedEthernetMatch) {
49         boolean verdict = true;
50         final Boolean checkNullValues = checkNullValues(statsEthernetMatch, storedEthernetMatch);
51         if (checkNullValues != null) {
52             verdict = checkNullValues;
53         } else {
54             verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetSource(),
55                     storedEthernetMatch.getEthernetSource());
56             if (verdict) {
57                 verdict = ethernetMatchFieldsEquals(statsEthernetMatch.getEthernetDestination(),
58                         storedEthernetMatch.getEthernetDestination());
59             }
60             if (verdict) {
61                 if (statsEthernetMatch.getEthernetType() == null) {
62                     if (storedEthernetMatch.getEthernetType() != null) {
63                         verdict = false;
64                     }
65                 } else {
66                     verdict = statsEthernetMatch.getEthernetType().equals(storedEthernetMatch.getEthernetType());
67                 }
68             }
69         }
70         return verdict;
71     }
72
73     static boolean ethernetMatchFieldsEquals(final MacAddressFilter statsEthernetMatchFields,
74             final MacAddressFilter storedEthernetMatchFields) {
75         boolean verdict = true;
76         final Boolean checkNullValues = checkNullValues(statsEthernetMatchFields, storedEthernetMatchFields);
77         if (checkNullValues != null) {
78             verdict = checkNullValues;
79         } else {
80             verdict = macAddressEquals(statsEthernetMatchFields.getAddress(), storedEthernetMatchFields.getAddress());
81             if (verdict) {
82                 verdict = macAddressEquals(statsEthernetMatchFields.getMask(), storedEthernetMatchFields.getMask());
83             }
84         }
85         return verdict;
86     }
87
88     static boolean macAddressEquals(final MacAddress statsMacAddress, final MacAddress storedMacAddress) {
89         boolean verdict = true;
90         final Boolean checkNullValues = checkNullValues(statsMacAddress, storedMacAddress);
91         if (checkNullValues != null) {
92             verdict = checkNullValues;
93         } else {
94             verdict = statsMacAddress.getValue().equalsIgnoreCase(storedMacAddress.getValue());
95         }
96         return verdict;
97     }
98
99     @VisibleForTesting
100     static boolean layer3MatchEquals(final Layer3Match statsLayer3Match, final Layer3Match storedLayer3Match) {
101         boolean verdict = true;
102         if (statsLayer3Match instanceof Ipv4Match && storedLayer3Match instanceof Ipv4Match) {
103             final Ipv4Match statsIpv4Match = (Ipv4Match) statsLayer3Match;
104             final Ipv4Match storedIpv4Match = (Ipv4Match) storedLayer3Match;
105             verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(storedIpv4Match.getIpv4Destination(),
106                     statsIpv4Match.getIpv4Destination());
107             if (verdict) {
108                 verdict = MatchComparatorHelper.compareIpv4PrefixNullSafe(statsIpv4Match.getIpv4Source(),
109                         storedIpv4Match.getIpv4Source());
110             }
111         } else if(statsLayer3Match instanceof Ipv6Match && storedLayer3Match instanceof Ipv6Match) {
112             final Ipv6Match statsIpv6Match = (Ipv6Match) statsLayer3Match;
113             final Ipv6Match storedIpv6Match = (Ipv6Match) storedLayer3Match;
114             verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(storedIpv6Match.getIpv6Destination(),
115                     statsIpv6Match.getIpv6Destination());
116             if (verdict) {
117                 verdict = MatchComparatorHelper.compareIpv6PrefixNullSafe(statsIpv6Match.getIpv6Source(),
118                         storedIpv6Match.getIpv6Source());
119             }
120         }else {
121             final Boolean nullCheckOut = checkNullValues(storedLayer3Match, statsLayer3Match);
122             if (nullCheckOut != null) {
123                 verdict = nullCheckOut;
124             } else {
125                 verdict = storedLayer3Match.equals(statsLayer3Match);
126             }
127         }
128
129         return verdict;
130     }
131
132
133     /**
134      * TODO: why don't we use the default Ipv4Prefix.equals()?
135      *
136      * @param statsIpAddress
137      * @param storedIpAddress
138      * @return true if IPv4prefixes equals
139      */
140     static boolean IpAddressEquals(final Ipv4Prefix statsIpAddress, final Ipv4Prefix storedIpAddress) {
141         final IntegerIpAddress statsIpAddressInt = MatchComparatorHelper.strIpToIntIp(statsIpAddress.getValue());
142         final IntegerIpAddress storedIpAddressInt = MatchComparatorHelper.strIpToIntIp(storedIpAddress.getValue());
143
144         if (ipAndMaskBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
145             return true;
146         }
147         if (ipBasedMatch(statsIpAddressInt, storedIpAddressInt)) {
148             return true;
149         }
150         return false;
151     }
152
153     static boolean ipAndMaskBasedMatch(final IntegerIpAddress statsIpAddressInt,
154             final IntegerIpAddress storedIpAddressInt) {
155         return ((statsIpAddressInt.getIp() & statsIpAddressInt.getMask()) == (storedIpAddressInt.getIp() & storedIpAddressInt
156                 .getMask()));
157     }
158
159     static boolean ipBasedMatch(final IntegerIpAddress statsIpAddressInt, final IntegerIpAddress storedIpAddressInt) {
160         return (statsIpAddressInt.getIp() == storedIpAddressInt.getIp());
161     }
162
163
164     private static boolean IpAddressEquals(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
165         final String[] statsIpMask = statsIpv6.getValue().split("/");
166         final String[] storedIpMask = storedIpv6.getValue().split("/");
167         if(! (statsIpMask.length > 1 && storedIpMask.length > 1 &&  statsIpMask[1].equals(storedIpMask[1]))){
168             return false;
169         }
170         if(InetAddresses.forString(statsIpMask[0]).equals(InetAddresses.forString(storedIpMask[0]))){
171             return true;
172         }
173         return false;
174     }
175
176
177     static Boolean checkNullValues(final Object v1, final Object v2) {
178         Boolean verdict = null;
179         if (v1 == null && v2 != null) {
180             verdict = Boolean.FALSE;
181         } else if (v1 != null && v2 == null) {
182             verdict = Boolean.FALSE;
183         } else if (v1 == null && v2 == null) {
184             verdict = Boolean.TRUE;
185         }
186
187         return verdict;
188     }
189
190     static boolean compareIpv4PrefixNullSafe(final Ipv4Prefix statsIpv4, final Ipv4Prefix storedIpv4) {
191         boolean verdict = true;
192         final Boolean checkDestNullValuesOut = checkNullValues(storedIpv4, statsIpv4);
193         if (checkDestNullValuesOut != null) {
194             verdict = checkDestNullValuesOut;
195         } else if (!IpAddressEquals(statsIpv4, storedIpv4)) {
196             verdict = false;
197         }
198
199         return verdict;
200     }
201
202     private static boolean compareIpv6PrefixNullSafe(Ipv6Prefix statsIpv6, Ipv6Prefix storedIpv6) {
203         boolean verdict = true;
204         final Boolean checkDestNullValuesOut = checkNullValues(statsIpv6, storedIpv6);
205         if (checkDestNullValuesOut != null) {
206             verdict = checkDestNullValuesOut;
207         } else if (!IpAddressEquals(statsIpv6, storedIpv6)) {
208             verdict = false;
209         }
210
211         return verdict;
212     }
213
214     /**
215      * Method return integer version of ip address. Converted int will be mask if mask specified
216      */
217     static IntegerIpAddress strIpToIntIp(final String ipAddresss) {
218
219         final String[] parts = ipAddresss.split("/");
220         final String ip = parts[0];
221         int prefix;
222
223         if (parts.length < 2) {
224             prefix = DEFAULT_SUBNET;
225         } else {
226             prefix = Integer.parseInt(parts[1]);
227             if (prefix < 0 || prefix > IPV4_MASK_LENGTH) {
228                 final StringBuilder stringBuilder = new StringBuilder(
229                         "Valid values for mask are from range 0 - 32. Value ");
230                 stringBuilder.append(prefix);
231                 stringBuilder.append(" is invalid.");
232                 throw new IllegalStateException(stringBuilder.toString());
233             }
234         }
235
236         IntegerIpAddress integerIpAddress = null;
237
238         final Inet4Address addr = ((Inet4Address) InetAddresses.forString(ip));
239         final byte[] addrBytes = addr.getAddress();
240         // FIXME: what is meaning of anding with 0xFF? Probably could be removed.
241         final int ipInt = ((addrBytes[POSITION_OCTET_1] & 0xFF) << SHIFT_OCTET_1)
242                 | ((addrBytes[POSITION_OCTET_2] & 0xFF) << SHIFT_OCTET_2)
243                 | ((addrBytes[POSITION_OCTET_3] & 0xFF) << SHIFT_OCTET_3)
244                 | ((addrBytes[POSITION_OCTET_4] & 0xFF) << SHIFT_OCTET_4);
245
246         // FIXME: Is this valid?
247         final int mask = 0xffffffff << DEFAULT_SUBNET - prefix;
248
249         integerIpAddress = new IntegerIpAddress(ipInt, mask);
250
251         return integerIpAddress;
252     }
253
254 }