Merge "Flat/VLAN network type support"
[netvirt.git] / vpnservice / aclservice / impl / src / main / java / org / opendaylight / netvirt / aclservice / utils / AclServiceOFFlowBuilder.java
1 /*
2  * Copyright (c) 2016 Red Hat, 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
9 package org.opendaylight.netvirt.aclservice.utils;
10
11 import java.util.ArrayList;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import org.opendaylight.genius.mdsalutil.MatchFieldType;
17 import org.opendaylight.genius.mdsalutil.MatchInfo;
18 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
19 import org.opendaylight.genius.mdsalutil.NwConstants;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRange;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.SourcePortRange;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 public class AclServiceOFFlowBuilder {
32
33     private static final Logger LOG =
34             LoggerFactory.getLogger(AclServiceOFFlowBuilder.class);
35
36     /**
37      * Converts IP matches into flows.
38      * @param matches
39      *            the matches
40      * @return the map containing the flows and the respective flow id
41      */
42     public static Map<String, List<MatchInfoBase>> programIpFlow(Matches matches) {
43         if (matches != null) {
44             AceIp acl = (AceIp) matches.getAceType();
45             Short protocol = acl.getProtocol();
46             if (protocol == null) {
47                 return programEtherFlow(acl);
48             } else if (acl.getProtocol() == NwConstants.IP_PROT_TCP) {
49                 return programTcpFlow(acl);
50             } else if (acl.getProtocol() == NwConstants.IP_PROT_UDP) {
51                 return programUdpFlow(acl);
52             } else if (acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
53                 return programIcmpFlow(acl);
54             } else if (acl.getProtocol() != -1) {
55                 return programOtherProtocolFlow(acl);
56             }
57         }
58         return null;
59     }
60
61     /** Converts ether  matches to flows.
62      * @param acl the access control list
63      * @return the map containing the flows and the respective flow id
64      */
65     public static Map<String,List<MatchInfoBase>> programEtherFlow(AceIp acl) {
66         List<MatchInfoBase> flowMatches = new ArrayList<>();
67         flowMatches.addAll(addSrcIpMatches(acl));
68         flowMatches.addAll(addDstIpMatches(acl));
69         String flowId = "ETHER" + acl.getProtocol();
70         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
71         flowMatchesMap.put(flowId,flowMatches);
72         return flowMatchesMap;
73     }
74
75     /** Converts generic protocol matches to flows.
76      *
77      * @param acl the access control list
78      * @return the map containing the flows and the respective flow id
79      */
80     public static Map<String,List<MatchInfoBase>> programOtherProtocolFlow(AceIp acl) {
81         List<MatchInfoBase> flowMatches = new ArrayList<>();
82         flowMatches.addAll(addSrcIpMatches(acl));
83         flowMatches.addAll(addDstIpMatches(acl));
84         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
85             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
86                 new long[] { NwConstants.ETHTYPE_IPV4 }));
87         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
88             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
89                 new long[] { NwConstants.ETHTYPE_IPV6 }));
90         }
91         flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
92             new long[] { acl.getProtocol() }));
93         String flowId = "OTHER_PROTO" + acl.getProtocol();
94         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
95         flowMatchesMap.put(flowId,flowMatches);
96         return flowMatchesMap;
97     }
98
99     /**Converts icmp matches to flows.
100      * @param acl the access control list
101      * @return the map containing the flows and the respective flow id
102      */
103     public static Map<String,List<MatchInfoBase>> programIcmpFlow(AceIp acl) {
104         List<MatchInfoBase> flowMatches = new ArrayList<>();
105         flowMatches.addAll(addSrcIpMatches(acl));
106         flowMatches.addAll(addDstIpMatches(acl));
107         //For ICMP port range indicates type and code
108         SourcePortRange sourcePortRange = acl.getSourcePortRange();
109         String flowId = "ICMP_";
110         if (sourcePortRange != null) {
111             if (acl.getAceIpVersion() instanceof AceIpv4 ) {
112                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v4,
113                     new long[] { sourcePortRange.getLowerPort().getValue(),
114                                  sourcePortRange.getUpperPort().getValue() }));
115                 flowId = flowId + "V4_SOURCE_" + sourcePortRange.getLowerPort().getValue()
116                         + sourcePortRange.getUpperPort().getValue();
117             } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
118                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v6,
119                     new long[] { sourcePortRange.getLowerPort().getValue(),
120                                  sourcePortRange.getUpperPort().getValue() }));
121                 flowId = flowId + "V6_SOURCE_" + sourcePortRange.getLowerPort().getValue() + "_"
122                         + sourcePortRange.getUpperPort().getValue() + "_";
123             }
124         }
125         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
126         if (destinationPortRange != null) {
127             if (acl.getAceIpVersion() instanceof AceIpv4 ) {
128                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v4,
129                     new long[] { destinationPortRange.getLowerPort().getValue(),
130                                  destinationPortRange.getUpperPort().getValue() }));
131                 flowId = flowId + "V4_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
132                         + destinationPortRange.getUpperPort().getValue() + "_";
133             } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
134                 flowMatches.add(new MatchInfo(MatchFieldType.icmp_v6,
135                     new long[] { destinationPortRange.getLowerPort().getValue(),
136                                  destinationPortRange.getUpperPort().getValue() }));
137                 flowId = flowId + "V6_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
138                         + destinationPortRange.getUpperPort().getValue() + "_";
139             }
140         }
141         flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
142             new long[] { acl.getProtocol() }));
143         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
144         flowMatchesMap.put(flowId,flowMatches);
145         return flowMatchesMap;
146     }
147
148     /**Converts TCP matches to flows.
149      * @param acl the access control list
150      * @return the map containing the flows and the respective flow id
151      */
152     public static Map<String,List<MatchInfoBase>> programTcpFlow(AceIp acl) {
153         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
154         SourcePortRange sourcePortRange = acl.getSourcePortRange();
155         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
156         if (sourcePortRange == null && destinationPortRange == null) {
157             List<MatchInfoBase> flowMatches = new ArrayList<>();
158             flowMatches.addAll(addSrcIpMatches(acl));
159             flowMatches.addAll(addDstIpMatches(acl));
160             flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
161                 new long[] { acl.getProtocol() }));
162             String flowId = "TCP_SOURCE_ALL_";
163             flowMatchesMap.put(flowId,flowMatches);
164             return flowMatchesMap;
165         }
166         if (sourcePortRange != null) {
167             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
168                 sourcePortRange.getUpperPort().getValue());
169             for (Integer port: portMaskMap.keySet()) {
170                 List<MatchInfoBase> flowMatches = new ArrayList<>();
171                 flowMatches.addAll(addSrcIpMatches(acl));
172                 flowMatches.addAll(addDstIpMatches(acl));
173                 flowMatches.add(new MatchInfo(MatchFieldType.tcp_src,
174                     new long[] { port}));
175                 /*flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_tcp_src_with_mask,
176                     new long[] {  port, portMaskMap.get(port) }));*/
177                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
178                     new long[] { acl.getProtocol() }));
179                 String flowId = "TCP_SOURCE_" + port + "_" + portMaskMap.get(port);
180                 flowMatchesMap.put(flowId,flowMatches);
181             }
182         }
183         if (destinationPortRange != null) {
184             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
185                 destinationPortRange.getUpperPort().getValue());
186             for (Integer port: portMaskMap.keySet()) {
187                 List<MatchInfoBase> flowMatches = new ArrayList<>();
188                 flowMatches.addAll(addSrcIpMatches(acl));
189                 flowMatches.addAll(addDstIpMatches(acl));
190                 flowMatches.add(new MatchInfo(MatchFieldType.tcp_dst,
191                         new long[] { port}));
192                 /*flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_tcp_dst_with_mask,
193                     new long[] {  port, portMaskMap.get(port) }));*/
194                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
195                     new long[] { acl.getProtocol() }));
196                 String flowId = "TCP_DESTINATION_" + port + "_" + portMaskMap.get(port);
197                 flowMatchesMap.put(flowId,flowMatches);
198             }
199         }
200         return flowMatchesMap;
201     }
202
203     /**Converts UDP matches to flows.
204      * @param acl the access control list
205      * @return the map containing the flows and the respective flow id
206      */
207     public static Map<String,List<MatchInfoBase>> programUdpFlow(AceIp acl) {
208         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
209         SourcePortRange sourcePortRange = acl.getSourcePortRange();
210         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
211         if (sourcePortRange == null && destinationPortRange == null) {
212             List<MatchInfoBase> flowMatches = new ArrayList<>();
213             flowMatches.addAll(addSrcIpMatches(acl));
214             flowMatches.addAll(addDstIpMatches(acl));
215             flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
216                 new long[] { acl.getProtocol() }));
217             String flowId = "UDP_SOURCE_ALL_";
218             flowMatchesMap.put(flowId,flowMatches);
219             return flowMatchesMap;
220         }
221         if (sourcePortRange != null) {
222             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
223                 sourcePortRange.getUpperPort().getValue());
224             for (Integer port: portMaskMap.keySet()) {
225                 List<MatchInfoBase> flowMatches = new ArrayList<>();
226                 flowMatches.addAll(addSrcIpMatches(acl));
227                 flowMatches.addAll(addDstIpMatches(acl));
228                 flowMatches.add(new MatchInfo(MatchFieldType.udp_src,
229                     new long[] { port}));
230                 /*flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_udp_src_with_mask,
231                     new long[] {  port, portMaskMap.get(port) }));*/
232                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
233                     new long[] { acl.getProtocol() }));
234                 String flowId = "UDP_SOURCE_" + port + "_" + portMaskMap.get(port);
235                 flowMatchesMap.put(flowId ,flowMatches);
236             }
237         }
238         if (destinationPortRange != null) {
239             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
240                 destinationPortRange.getUpperPort().getValue());
241             for (Integer port: portMaskMap.keySet()) {
242                 List<MatchInfoBase> flowMatches = new ArrayList<>();
243                 flowMatches.addAll(addSrcIpMatches(acl));
244                 flowMatches.addAll(addDstIpMatches(acl));
245                 flowMatches.add(new MatchInfo(MatchFieldType.udp_dst,
246                     new long[] { port}));
247                 /*flowMatches.add(new NxMatchInfo(NxMatchFieldType.nx_udp_dst_with_mask,
248                     new long[] {  port, portMaskMap.get(port) }));*/
249                 flowMatches.add(new MatchInfo(MatchFieldType.ip_proto,
250                     new long[] { acl.getProtocol() }));
251                 String flowId = "UDP_DESTINATION_" + port + "_" + portMaskMap.get(port);
252                 flowMatchesMap.put(flowId, flowMatches);
253             }
254         }
255
256         return flowMatchesMap;
257     }
258
259     /** Adds source ip matches to the flows.
260      * @param acl the access control list
261      * @return the list of flows.
262      */
263     public static List<MatchInfoBase> addSrcIpMatches(AceIp acl) {
264         List<MatchInfoBase> flowMatches = new ArrayList<>();
265         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
266             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
267                 new long[] { NwConstants.ETHTYPE_IPV4 }));
268             Ipv4Prefix srcNetwork = ((AceIpv4)acl.getAceIpVersion()).getSourceIpv4Network();
269             if (null != srcNetwork) {
270                 String[] ipaddressValues = srcNetwork.getValue().split("/");
271                 flowMatches.add(new MatchInfo(MatchFieldType.ipv4_source,
272                     new String[] {ipaddressValues[0], ipaddressValues[1]}));
273             }
274         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
275             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
276                 new long[] { NwConstants.ETHTYPE_IPV6 }));
277             Ipv6Prefix srcNetwork = ((AceIpv6)acl.getAceIpVersion()).getSourceIpv6Network();
278             if (null != srcNetwork) {
279                 // TODO Ipv6 Match.
280             }
281         }
282         return flowMatches;
283     }
284
285     /** Adds destination ip matches to the flows.
286      * @param acl the access control list
287      * @return the list of flows.
288      */
289     public static List<MatchInfoBase> addDstIpMatches(AceIp acl) {
290         List<MatchInfoBase> flowMatches = new ArrayList<>();
291         if (acl.getAceIpVersion() instanceof AceIpv4 ) {
292             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
293                 new long[] { NwConstants.ETHTYPE_IPV4 }));
294             Ipv4Prefix dstNetwork = ((AceIpv4)acl.getAceIpVersion()).getDestinationIpv4Network();
295             if (null != dstNetwork) {
296                 String[] ipaddressValues = dstNetwork.getValue().split("/");
297                 flowMatches.add(new MatchInfo(MatchFieldType.ipv4_destination,
298                     new String[] {ipaddressValues[0], ipaddressValues[1]}));
299             }
300         } else if (acl.getAceIpVersion() instanceof AceIpv6 ) {
301             flowMatches.add(new MatchInfo(MatchFieldType.eth_type,
302                 new long[] { NwConstants.ETHTYPE_IPV6 }));
303             Ipv6Prefix dstNetwork = ((AceIpv6)acl.getAceIpVersion()).getDestinationIpv6Network();
304             if (null != dstNetwork) {
305              // TODO Ipv6 Match.
306             }
307         }
308         return flowMatches;
309     }
310
311     /**
312      * Converts port range into a set of masked port ranges.
313      *
314      * @param portMin the starting port of the range.
315      * @param portMax the ending port of the range.
316      * @return the map containing the port no and their mask.
317      *
318      */
319     public static Map<Integer,Integer>  getLayer4MaskForRange(int portMin, int portMax) {
320         final int[] offset = { 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 };
321         final int[] mask = { 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
322             0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF };
323         int noOfPorts = portMax - portMin + 1;
324         Map<Integer,Integer> portMap = new HashMap<>();
325         if (noOfPorts == 1) {
326             portMap.put(portMin, mask[15]);
327             return portMap;
328         }
329         if (noOfPorts < 0) { // TODO: replace with infrautils.counter in case of high repetitive usage
330             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
331                     portMax);
332             return portMap;
333         }
334         String binaryNoOfPorts = Integer.toBinaryString(noOfPorts);
335         if (binaryNoOfPorts.length() > 16) { // TODO: replace with infrautils.counter in case of high repetitive usage
336             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
337                     portMax);
338             return portMap;
339         }
340         int medianOffset = 16 - binaryNoOfPorts.length();
341         int medianLength = offset[medianOffset];
342         int median = 0;
343         for (int tempMedian = 0;tempMedian < portMax;) {
344             tempMedian = medianLength + tempMedian;
345             if (portMin < tempMedian) {
346                 median = tempMedian;
347                 break;
348             }
349         }
350         int tempMedian = 0;
351         int currentMedain = median;
352         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
353             tempMedian = currentMedain - offset[tempMedianOffset];
354             if (portMin <= tempMedian) {
355                 for (;portMin <= tempMedian;) {
356                     portMap.put(tempMedian, mask[tempMedianOffset]);
357                     currentMedain = tempMedian;
358                     tempMedian = tempMedian - offset[tempMedianOffset];
359                 }
360             }
361         }
362         currentMedain = median;
363         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
364             tempMedian = currentMedain + offset[tempMedianOffset];
365             if (portMax >= tempMedian - 1) {
366                 for (;portMax >= tempMedian - 1;) {
367                     portMap.put(currentMedain, mask[tempMedianOffset]);
368                     currentMedain = tempMedian;
369                     tempMedian = tempMedian  + offset[tempMedianOffset];
370                 }
371             }
372         }
373         return portMap;
374     }
375 }