Switch to JDT annotations for Nullable and NonNull
[netvirt.git] / 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 import java.util.Map.Entry;
16 import java.util.function.BiFunction;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.InstructionInfo;
20 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
21 import org.opendaylight.genius.mdsalutil.NwConstants;
22 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
23 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
24 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
25 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
26 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
27 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv4;
28 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
29 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
30 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
31 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Source;
32 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Destination;
33 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
34 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchCtState;
35 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchInfoHelper;
36 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchTcpDestinationPort;
37 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchTcpSourcePort;
38 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchUdpDestinationPort;
39 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchUdpSourcePort;
40 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;
41 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;
42 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;
43 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;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.DestinationPortRange;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160218.acl.transport.header.fields.SourcePortRange;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeBase;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public final class AclServiceOFFlowBuilder {
53     private static final Logger LOG = LoggerFactory.getLogger(AclServiceOFFlowBuilder.class);
54
55     private AclServiceOFFlowBuilder() {
56     }
57
58     /**
59      * Converts IP matches into flows.
60      * @param matches
61      *            the matches
62      * @return the map containing the flows and the respective flow id
63      */
64     @Nullable
65     public static Map<String, List<MatchInfoBase>> programIpFlow(Matches matches) {
66         if (matches != null) {
67             AceIp acl = (AceIp) matches.getAceType();
68             Short protocol = acl.getProtocol();
69             if (protocol == null) {
70                 return programEtherFlow(acl);
71             } else if (acl.getProtocol() == NwConstants.IP_PROT_TCP) {
72                 return programTcpFlow(acl);
73             } else if (acl.getProtocol() == NwConstants.IP_PROT_UDP) {
74                 return programUdpFlow(acl);
75             } else if (acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
76                 return programIcmpFlow(acl);
77             } else if (acl.getProtocol() != -1) {
78                 return programOtherProtocolFlow(acl);
79             }
80         }
81         return null;
82     }
83
84     /** Converts ether  matches to flows.
85      * @param acl the access control list
86      * @return the map containing the flows and the respective flow id
87      */
88     public static Map<String,List<MatchInfoBase>> programEtherFlow(AceIp acl) {
89         List<MatchInfoBase> flowMatches = new ArrayList<>();
90         flowMatches.addAll(addSrcIpMatches(acl));
91         flowMatches.addAll(addDstIpMatches(acl));
92         String flowId = "ETHER" + acl.getProtocol();
93         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
94         flowMatchesMap.put(flowId,flowMatches);
95         return flowMatchesMap;
96     }
97
98     /** Converts generic protocol matches to flows.
99      *
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>> programOtherProtocolFlow(AceIp acl) {
104         List<MatchInfoBase> flowMatches = new ArrayList<>();
105         flowMatches.addAll(addSrcIpMatches(acl));
106         flowMatches.addAll(addDstIpMatches(acl));
107         if (acl.getAceIpVersion() instanceof AceIpv4) {
108             flowMatches.add(MatchEthernetType.IPV4);
109         } else if (acl.getAceIpVersion() instanceof AceIpv6) {
110             flowMatches.add(MatchEthernetType.IPV6);
111         }
112         flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
113         String flowId = "OTHER_PROTO" + acl.getProtocol();
114         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
115         flowMatchesMap.put(flowId,flowMatches);
116         return flowMatchesMap;
117     }
118
119     /**Converts icmp matches to flows.
120      * @param acl the access control list
121      * @return the map containing the flows and the respective flow id
122      */
123     public static Map<String,List<MatchInfoBase>> programIcmpFlow(AceIp acl) {
124         List<MatchInfoBase> flowMatches = new ArrayList<>();
125         flowMatches.addAll(addSrcIpMatches(acl));
126         flowMatches.addAll(addDstIpMatches(acl));
127         //For ICMP port range indicates type and code
128         SourcePortRange sourcePortRange = acl.getSourcePortRange();
129         String flowId = "ICMP_";
130         if (sourcePortRange != null) {
131             if (acl.getAceIpVersion() instanceof AceIpv4) {
132                 flowMatches.add(new MatchIcmpv4(sourcePortRange.getLowerPort().getValue().shortValue(),
133                                  sourcePortRange.getUpperPort().getValue().shortValue()));
134                 flowId = flowId + "V4_SOURCE_" + sourcePortRange.getLowerPort().getValue()
135                         + sourcePortRange.getUpperPort().getValue();
136             } else if (acl.getAceIpVersion() instanceof AceIpv6) {
137                 flowMatches.add(new MatchIcmpv6(sourcePortRange.getLowerPort().getValue().shortValue(),
138                                  sourcePortRange.getUpperPort().getValue().shortValue()));
139                 flowId = flowId + "V6_SOURCE_" + sourcePortRange.getLowerPort().getValue() + "_"
140                         + sourcePortRange.getUpperPort().getValue() + "_";
141             }
142         }
143         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
144         if (destinationPortRange != null) {
145             if (acl.getAceIpVersion() instanceof AceIpv4) {
146                 flowMatches.add(new MatchIcmpv4(destinationPortRange.getLowerPort().getValue().shortValue(),
147                                  destinationPortRange.getUpperPort().getValue().shortValue()));
148                 flowId = flowId + "V4_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
149                         + destinationPortRange.getUpperPort().getValue() + "_";
150             } else if (acl.getAceIpVersion() instanceof AceIpv6) {
151                 flowMatches.add(new MatchIcmpv6(destinationPortRange.getLowerPort().getValue().shortValue(),
152                                  destinationPortRange.getUpperPort().getValue().shortValue()));
153                 flowId = flowId + "V6_DESTINATION_" + destinationPortRange.getLowerPort().getValue()
154                         + destinationPortRange.getUpperPort().getValue() + "_";
155             }
156         }
157
158         if (acl.getAceIpVersion() instanceof AceIpv6 && acl.getProtocol() == NwConstants.IP_PROT_ICMP) {
159             // We are aligning our implementation similar to Neutron Firewall driver where a Security
160             // Group rule with "Ethertype as IPv6 and Protocol as icmp" is treated as ICMPV6 SG Rule.
161             flowMatches.add(new MatchIpProtocol(AclConstants.IP_PROT_ICMPV6));
162         } else {
163             flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
164         }
165         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
166         flowMatchesMap.put(flowId,flowMatches);
167         return flowMatchesMap;
168     }
169
170     /**Converts TCP matches to flows.
171      * @param acl the access control list
172      * @return the map containing the flows and the respective flow id
173      */
174     public static Map<String,List<MatchInfoBase>> programTcpFlow(AceIp acl) {
175         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
176         SourcePortRange sourcePortRange = acl.getSourcePortRange();
177         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
178         if (sourcePortRange == null && destinationPortRange == null) {
179             List<MatchInfoBase> flowMatches = new ArrayList<>();
180             flowMatches.addAll(addSrcIpMatches(acl));
181             flowMatches.addAll(addDstIpMatches(acl));
182             flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
183             String flowId = "TCP_SOURCE_ALL_";
184             flowMatchesMap.put(flowId,flowMatches);
185             return flowMatchesMap;
186         }
187         if (sourcePortRange != null) {
188             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
189                 sourcePortRange.getUpperPort().getValue());
190             updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchTcpSourcePort::new, "TCP_SOURCE_");
191         }
192         if (destinationPortRange != null) {
193             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
194                 destinationPortRange.getUpperPort().getValue());
195             updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchTcpDestinationPort::new, "TCP_DESTINATION_");
196         }
197         return flowMatchesMap;
198     }
199
200     /**Converts UDP matches to flows.
201      * @param acl the access control list
202      * @return the map containing the flows and the respective flow id
203      */
204     public static Map<String,List<MatchInfoBase>> programUdpFlow(AceIp acl) {
205         Map<String,List<MatchInfoBase>> flowMatchesMap = new HashMap<>();
206         SourcePortRange sourcePortRange = acl.getSourcePortRange();
207         DestinationPortRange destinationPortRange = acl.getDestinationPortRange();
208         if (sourcePortRange == null && destinationPortRange == null) {
209             List<MatchInfoBase> flowMatches = new ArrayList<>();
210             flowMatches.addAll(addSrcIpMatches(acl));
211             flowMatches.addAll(addDstIpMatches(acl));
212             flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
213             String flowId = "UDP_SOURCE_ALL_";
214             flowMatchesMap.put(flowId,flowMatches);
215             return flowMatchesMap;
216         }
217         if (sourcePortRange != null) {
218             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(sourcePortRange.getLowerPort().getValue(),
219                 sourcePortRange.getUpperPort().getValue());
220             updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchUdpSourcePort::new, "UDP_SOURCE_");
221         }
222         if (destinationPortRange != null) {
223             Map<Integer, Integer> portMaskMap = getLayer4MaskForRange(destinationPortRange.getLowerPort().getValue(),
224                 destinationPortRange.getUpperPort().getValue());
225             updateFlowMatches(acl, portMaskMap, flowMatchesMap, NxMatchUdpDestinationPort::new, "UDP_DESTINATION_");
226         }
227
228         return flowMatchesMap;
229     }
230
231     private static void updateFlowMatches(AceIp acl, Map<Integer, Integer> portMaskMap,
232             Map<String, List<MatchInfoBase>> flowMatchesMap,
233             BiFunction<Integer, Integer, NxMatchInfoHelper> constructor, String key) {
234         for (Entry<Integer, Integer> entry: portMaskMap.entrySet()) {
235             Integer port = entry.getKey();
236             List<MatchInfoBase> flowMatches = new ArrayList<>();
237             flowMatches.addAll(addSrcIpMatches(acl));
238             flowMatches.addAll(addDstIpMatches(acl));
239             Integer mask = entry.getValue();
240             if (mask != AclConstants.ALL_LAYER4_PORT_MASK) {
241                 flowMatches.add(constructor.apply(port, mask));
242             }
243             flowMatches.add(new MatchIpProtocol(acl.getProtocol()));
244             String flowId = key + port + "_" + mask;
245             flowMatchesMap.put(flowId, flowMatches);
246         }
247     }
248
249     /** Adds source ip matches to the flows.
250      * @param acl the access control list
251      * @return the list of flows.
252      */
253     public static List<MatchInfoBase> addSrcIpMatches(AceIp acl) {
254         List<MatchInfoBase> flowMatches = new ArrayList<>();
255         if (acl.getAceIpVersion() instanceof AceIpv4) {
256             flowMatches.add(MatchEthernetType.IPV4);
257             Ipv4Prefix srcNetwork = ((AceIpv4)acl.getAceIpVersion()).getSourceIpv4Network();
258             if (null != srcNetwork && !srcNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
259                 flowMatches.add(new MatchIpv4Source(srcNetwork));
260             }
261         } else if (acl.getAceIpVersion() instanceof AceIpv6) {
262             flowMatches.add(MatchEthernetType.IPV6);
263             Ipv6Prefix srcNetwork = ((AceIpv6)acl.getAceIpVersion()).getSourceIpv6Network();
264             if (null != srcNetwork && !srcNetwork.getValue().equals(AclConstants.IPV6_ALL_NETWORK)) {
265                 flowMatches.add(new MatchIpv6Source(srcNetwork));
266             }
267         }
268         return flowMatches;
269     }
270
271     /** Adds destination ip matches to the flows.
272      * @param acl the access control list
273      * @return the list of flows.
274      */
275     public static List<MatchInfoBase> addDstIpMatches(AceIp acl) {
276         List<MatchInfoBase> flowMatches = new ArrayList<>();
277         if (acl.getAceIpVersion() instanceof AceIpv4) {
278             flowMatches.add(MatchEthernetType.IPV4);
279             Ipv4Prefix dstNetwork = ((AceIpv4)acl.getAceIpVersion()).getDestinationIpv4Network();
280             if (null != dstNetwork && !dstNetwork.getValue().equals(AclConstants.IPV4_ALL_NETWORK)) {
281                 flowMatches.add(new MatchIpv4Destination(dstNetwork));
282             }
283         } else if (acl.getAceIpVersion() instanceof AceIpv6) {
284             flowMatches.add(MatchEthernetType.IPV6);
285             Ipv6Prefix dstNetwork = ((AceIpv6)acl.getAceIpVersion()).getDestinationIpv6Network();
286             if (null != dstNetwork && !dstNetwork.getValue().equals(AclConstants.IPV6_ALL_NETWORK)) {
287                 flowMatches.add(new MatchIpv6Destination(dstNetwork));
288             }
289         }
290         return flowMatches;
291     }
292
293     /** Adds LPort matches to the flow.
294      * @param lportTag lport tag
295      * @param conntrackState conntrack state to be used with matches
296      * @param conntrackMask conntrack mask to be used with matches
297      * @param serviceMode ingress or egress service
298      * @return list of matches
299      */
300     public static List<MatchInfoBase> addLPortTagMatches(int lportTag, int conntrackState, int conntrackMask,
301             Class<? extends ServiceModeBase> serviceMode) {
302         List<MatchInfoBase> matches = new ArrayList<>();
303         matches.add(AclServiceUtils.buildLPortTagMatch(lportTag, serviceMode));
304         matches.add(new NxMatchCtState(conntrackState, conntrackMask));
305         return matches;
306     }
307
308     /** Returns drop instruction info.
309      * @return drop list of InstructionInfo objects
310      */
311     public static List<InstructionInfo> getDropInstructionInfo() {
312         List<InstructionInfo> instructions = new ArrayList<>();
313         List<ActionInfo> actionsInfos = new ArrayList<>();
314         actionsInfos.add(new ActionDrop());
315         instructions.add(new InstructionApplyActions(actionsInfos));
316         return instructions;
317     }
318
319     /**
320      * Returns resubmit instruction info to the given table ID.
321      *
322      * @param tableId the table id
323      * @return resubmit list of InstructionInfo objects
324      */
325     public static List<InstructionInfo> getResubmitInstructionInfo(short tableId) {
326         List<InstructionInfo> instructions = new ArrayList<>();
327         List<ActionInfo> actionsInfos = new ArrayList<>();
328         actionsInfos.add(new ActionNxResubmit(tableId));
329         instructions.add(new InstructionApplyActions(actionsInfos));
330         return instructions;
331     }
332
333     /**
334      * Gets the goto instruction info which specifies goto to the specified
335      * table.
336      *
337      * @param gotoTableId the goto table id
338      * @return the goto instruction info
339      */
340     public static List<InstructionInfo> getGotoInstructionInfo(short gotoTableId) {
341         List<InstructionInfo> instructions = new ArrayList<>();
342         instructions.add(new InstructionGotoTable(gotoTableId));
343         return instructions;
344     }
345
346     /**
347      * Converts port range into a set of masked port ranges.
348      *
349      * @param portMin the starting port of the range.
350      * @param portMax the ending port of the range.
351      * @return the map containing the port no and their mask.
352      *
353      */
354     public static Map<Integer,Integer>  getLayer4MaskForRange(int portMin, int portMax) {
355         final int[] offset = { 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 };
356         final int[] mask = { 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
357             0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF };
358         int noOfPorts = portMax - portMin + 1;
359         Map<Integer,Integer> portMap = new HashMap<>();
360         if (noOfPorts == 1) {
361             portMap.put(portMin, mask[15]);
362             return portMap;
363         } else if (noOfPorts == AclConstants.ALL_LAYER4_PORT) {
364             portMap.put(portMin, AclConstants.ALL_LAYER4_PORT_MASK);
365             return portMap;
366         }
367         if (noOfPorts < 0) { // TODO: replace with infrautils.counter in case of high repetitive usage
368             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
369                     portMax);
370             return portMap;
371         }
372         String binaryNoOfPorts = Integer.toBinaryString(noOfPorts);
373         if (binaryNoOfPorts.length() > 16) { // TODO: replace with infrautils.counter in case of high repetitive usage
374             LOG.warn("Cannot convert port range into a set of masked port ranges - Illegal port range {}-{}", portMin,
375                     portMax);
376             return portMap;
377         }
378         int medianOffset = 16 - binaryNoOfPorts.length();
379         int medianLength = offset[medianOffset];
380         int median = 0;
381         for (int tempMedian = 0;tempMedian < portMax;) {
382             tempMedian = medianLength + tempMedian;
383             if (portMin < tempMedian) {
384                 median = tempMedian;
385                 break;
386             }
387         }
388         int tempMedian = 0;
389         int currentMedain = median;
390         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
391             tempMedian = currentMedain - offset[tempMedianOffset];
392             for (;portMin <= tempMedian;) {
393                 portMap.put(tempMedian, mask[tempMedianOffset]);
394                 currentMedain = tempMedian;
395                 tempMedian = tempMedian - offset[tempMedianOffset];
396             }
397         }
398         currentMedain = median;
399         for (int tempMedianOffset = medianOffset;16 > tempMedianOffset;tempMedianOffset++) {
400             tempMedian = currentMedain + offset[tempMedianOffset];
401             for (;portMax >= tempMedian - 1;) {
402                 portMap.put(currentMedain, mask[tempMedianOffset]);
403                 currentMedain = tempMedian;
404                 tempMedian = tempMedian  + offset[tempMedianOffset];
405             }
406         }
407         return portMap;
408     }
409
410 }