Classifiers - implementation changes.
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / sf / L4Classifier.java
1 /*
2  * Copyright (c) 2014 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
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
10
11
12
13 import java.util.ArrayList;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierName;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.Description;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter.Type;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.ParameterBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinitionBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.parameter.value.RangeValue;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer4Match;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.SctpMatch;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.SctpMatchBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatchBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatchBuilder;
38
39 import com.google.common.collect.ImmutableList;
40
41 /**
42  * Match against TCP or UDP, and source and/or destination ports
43  */
44 public class L4Classifier extends Classifier {
45
46     public static final String SPORT = "sourceport";
47     public static final String SPORT_RANGE = "sourceport_range";
48     public static final String DPORT = "destport";
49     public static final String DPORT_RANGE = "destport_range";
50     public static final ClassifierDefinitionId ID = new ClassifierDefinitionId("4250ab32-e8b8-445a-aebb-e1bd2cdd291f");
51     private static final ClassifierDefinition DEF = new ClassifierDefinitionBuilder().setId(
52             new ClassifierDefinitionId("4250ab32-e8b8-445a-aebb-e1bd2cdd291f"))
53         .setParent(IpProtoClassifier.ID)
54         .setName(new ClassifierName("l4"))
55         .setDescription(new Description("Match on the port number of UDP or TCP traffic"))
56         .setParameter(
57                 ImmutableList.of(
58                         new ParameterBuilder().setName(new ParameterName(SPORT))
59                             .setDescription(new Description("The source port number to match against"))
60                             .setType(Type.Int)
61                             .build(),
62                         new ParameterBuilder().setName(new ParameterName(SPORT_RANGE))
63                             .setDescription(new Description("The source port range to match against"))
64                             .setType(Type.Range)
65                             .build(),
66                         new ParameterBuilder().setName(new ParameterName(DPORT))
67                             .setDescription(new Description("The destination port number to match against"))
68                             .setType(Type.Int)
69                             .build(),
70                         new ParameterBuilder().setName(new ParameterName(DPORT_RANGE))
71                             .setDescription(new Description("The destination port range to match against"))
72                             .setType(Type.Range)
73                             .build()))
74         .build();
75
76     protected L4Classifier(Classifier parent) {
77         super(parent);
78     }
79
80     @Override
81     public ClassifierDefinitionId getId() {
82         return ID;
83     }
84
85     @Override
86     public ClassifierDefinition getClassDef() {
87         return DEF;
88     }
89
90     @Override
91     protected void checkPresenceOfRequiredParams(Map<String, ParameterValue> params) {
92         if (params.get(SPORT) != null && params.get(SPORT_RANGE) != null) {
93             throw new IllegalArgumentException("Classifier: {" + this.getClassDef().getName()
94                     + "}+. Illegal source port parameters: 'int' and 'range' values are mutually exclusive.");
95         }
96         if (params.get(DPORT) != null && params.get(DPORT_RANGE) != null) {
97             throw new IllegalArgumentException("Classifier: {" + this.getClassDef().getName()
98                     + "}+. Illegal destination port parameters: 'int' and 'range' values are mutually exclusive.");
99         }
100         if (params.get(SPORT) != null) {
101             if (params.get(SPORT).getIntValue() == null) {
102                 throw new IllegalArgumentException("Classifier: {" + this.getClassDef().getName()
103                         + "}+ Value of sourceport parameter is not present.");
104             }
105         }
106         if (params.get(SPORT_RANGE) != null) {
107             if (params.get(SPORT_RANGE) != null) {
108                 validateRangeValue(params.get(SPORT_RANGE).getRangeValue());
109             }
110         }
111
112         if (params.get(DPORT) != null) {
113             if (params.get(DPORT).getIntValue() == null) {
114                 throw new IllegalArgumentException("Classifier: {" + this.getClassDef().getName()
115                         + "}+ Value of destport parameter is not present.");
116             }
117         }
118         if (params.get(DPORT_RANGE) != null) {
119             if (params.get(DPORT_RANGE) != null) {
120                 validateRangeValue(params.get(DPORT_RANGE).getRangeValue());
121             }
122         }
123     }
124
125     private void validateRangeValue(RangeValue rangeValueParam) {
126         if (rangeValueParam == null) {
127             throw new IllegalArgumentException("Classifier: {" + this.getClassDef().getName()
128                     + "}+ Range value is not present.");
129         }
130         final Long min = rangeValueParam.getMin();
131         final Long max = rangeValueParam.getMax();
132         if (min > max) {
133             throw new IllegalArgumentException("Range value mismatch. MIN {" + min + "} is greater than MAX {" + max
134                     + "}.");
135         }
136     }
137
138     @Override
139     public List<MatchBuilder> update(List<MatchBuilder> matches, Map<String, ParameterValue> params) {
140         Set<Long> sPorts = new HashSet<>();
141         Set<Long> dPorts = new HashSet<>();
142         if (params.get(SPORT) != null) {
143             sPorts.add(params.get(SPORT).getIntValue());
144         } else if (params.get(SPORT_RANGE) != null) {
145             sPorts.addAll(createSetFromRange(params.get(SPORT_RANGE).getRangeValue()));
146         }
147         if (params.get(DPORT) != null) {
148             dPorts.add(params.get(DPORT).getIntValue());
149         } else if (params.get(DPORT_RANGE) != null) {
150             dPorts.addAll(createSetFromRange(params.get(DPORT_RANGE).getRangeValue()));
151         }
152
153         List<MatchBuilder> newMatches = new ArrayList<>();
154         for (MatchBuilder matchBuilder : matches) {
155             Layer4Match l4Match = matchBuilder.getLayer4Match();
156             Set<? extends Layer4Match> l4Matches = null;
157             if (l4Match == null) {
158                 l4Match = resolveL4Match(params);
159             }
160             if (l4Match instanceof UdpMatch) {
161                 l4Matches = createUdpMatches((UdpMatch) l4Match, sPorts, dPorts);
162             } else if (l4Match instanceof TcpMatch) {
163                 l4Matches = createTcpMatches((TcpMatch) l4Match, sPorts, dPorts);
164             } else if (l4Match instanceof SctpMatch) {
165                 l4Matches = createSctpMatches((SctpMatch) l4Match, sPorts, dPorts);
166             }
167             for (Layer4Match newL4Match : l4Matches) {
168                 newMatches.add(new MatchBuilder(matchBuilder.build()).setLayer4Match(newL4Match));
169             }
170         }
171         return newMatches;
172     }
173
174     private Layer4Match resolveL4Match(Map<String, ParameterValue> params) {
175         Long ipProto = IpProtoClassifier.getIpProtoValue(params);
176         if (ipProto == null) {
177             throw new IllegalArgumentException("Classifier-instance " + this.getClassDef().getName()
178                     + ": L4 protocol is null.");
179         }
180         if (IpProtoClassifier.UDP.equals(ipProto)) {
181             return new UdpMatchBuilder().build();
182         } else if (IpProtoClassifier.TCP.equals(ipProto)) {
183             return new TcpMatchBuilder().build();
184         } else if (IpProtoClassifier.SCTP.equals(ipProto)) {
185             return new SctpMatchBuilder().build();
186         }
187         throw new IllegalArgumentException("Unsupported L4 protocol.");
188     }
189
190     private Set<Long> createSetFromRange(RangeValue rangeValueParam) {
191         Set<Long> res = new HashSet<>();
192         if (rangeValueParam != null) {
193             final Long min = rangeValueParam.getMin();
194             final Long max = rangeValueParam.getMax();
195             for (long val = min; val <= max; val++) {
196                 res.add(val);
197             }
198         }
199         return res;
200     }
201
202     private Set<UdpMatch> createUdpMatches(UdpMatch udpMatch, Set<Long> sPorts, Set<Long> dPorts) {
203         Set<UdpMatch> udpMatches = new HashSet<>();
204         if (!sPorts.isEmpty() && dPorts.isEmpty()) {
205             for (Long srcPort : sPorts) {
206                 equalOrNotSetValidation(udpMatch.getUdpSourcePort(), srcPort.longValue());
207                 udpMatches.add(new UdpMatchBuilder(udpMatch).setUdpSourcePort(new PortNumber(srcPort.intValue())).build());
208             }
209         } else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
210             for (Long dstPort : dPorts) {
211                 equalOrNotSetValidation(udpMatch.getUdpDestinationPort(), dstPort.longValue());
212                 udpMatches.add(new UdpMatchBuilder(udpMatch).setUdpDestinationPort(new PortNumber(dstPort.intValue()))
213                     .build());
214             }
215         } else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
216             for (Long srcPort : sPorts) {
217                 for (Long dstPort : dPorts) {
218                     equalOrNotSetValidation(udpMatch.getUdpSourcePort(), srcPort.longValue());
219                     equalOrNotSetValidation(udpMatch.getUdpDestinationPort(), dstPort.longValue());
220                     udpMatches.add(new UdpMatchBuilder(udpMatch).setUdpSourcePort(new PortNumber(srcPort.intValue()))
221                         .setUdpDestinationPort(new PortNumber(dstPort.intValue()))
222                         .build());
223                 }
224             }
225         }
226         return udpMatches;
227     }
228
229     private Set<TcpMatch> createTcpMatches(TcpMatch tcpMatch, Set<Long> sPorts, Set<Long> dPorts) {
230         Set<TcpMatch> tcpMatches = new HashSet<>();
231         if (!sPorts.isEmpty() && dPorts.isEmpty()) {
232             for (Long srcPort : sPorts) {
233                 equalOrNotSetValidation(tcpMatch.getTcpSourcePort(), srcPort.longValue());
234                 tcpMatches.add(new TcpMatchBuilder(tcpMatch).setTcpSourcePort(new PortNumber(srcPort.intValue())).build());
235             }
236         } else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
237             for (Long dstPort : dPorts) {
238                 equalOrNotSetValidation(tcpMatch.getTcpDestinationPort(), dstPort.longValue());
239                 tcpMatches.add(new TcpMatchBuilder(tcpMatch).setTcpDestinationPort(new PortNumber(dstPort.intValue()))
240                     .build());
241             }
242         } else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
243             for (Long srcPort : sPorts) {
244                 for (Long dstPort : dPorts) {
245                     equalOrNotSetValidation(tcpMatch.getTcpSourcePort(), srcPort.longValue());
246                     equalOrNotSetValidation(tcpMatch.getTcpDestinationPort(), dstPort.longValue());
247                     tcpMatches.add(new TcpMatchBuilder(tcpMatch).setTcpSourcePort(new PortNumber(srcPort.intValue()))
248                         .setTcpDestinationPort(new PortNumber(dstPort.intValue()))
249                         .build());
250                 }
251             }
252         }
253         return tcpMatches;
254     }
255
256     private Set<SctpMatch> createSctpMatches(SctpMatch sctpMatch, Set<Long> sPorts, Set<Long> dPorts) {
257         Set<SctpMatch> sctpMatches = new HashSet<>();
258         if (!sPorts.isEmpty() && dPorts.isEmpty()) {
259             for (Long srcPort : sPorts) {
260                 equalOrNotSetValidation(sctpMatch.getSctpSourcePort(), srcPort.longValue());
261                 sctpMatches.add(new SctpMatchBuilder(sctpMatch).setSctpSourcePort(new PortNumber(srcPort.intValue()))
262                     .build());
263             }
264         } else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
265             for (Long dstPort : dPorts) {
266                 equalOrNotSetValidation(sctpMatch.getSctpDestinationPort(), dstPort.longValue());
267                 sctpMatches.add(new SctpMatchBuilder(sctpMatch).setSctpDestinationPort(new PortNumber(dstPort.intValue()))
268                     .build());
269             }
270         } else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
271             for (Long srcPort : sPorts) {
272                 for (Long dstPort : dPorts) {
273                     equalOrNotSetValidation(sctpMatch.getSctpSourcePort(), srcPort.longValue());
274                     equalOrNotSetValidation(sctpMatch.getSctpDestinationPort(), dstPort.longValue());
275                     sctpMatches.add(new SctpMatchBuilder(sctpMatch).setSctpSourcePort(new PortNumber(srcPort.intValue()))
276                         .setSctpDestinationPort(new PortNumber(dstPort.intValue()))
277                         .build());
278                 }
279             }
280         }
281         return sctpMatches;
282     }
283
284     private void equalOrNotSetValidation(PortNumber portInMatch, long paramValue) {
285         if (portInMatch != null) {
286             if (paramValue != portInMatch.getValue().longValue()) {
287                 throw new IllegalArgumentException("Classification conflict at " + this.getClassDef().getName()
288                         + ": Trying to override port value: " + portInMatch.getValue().longValue() + " by value "
289                         + paramValue);
290             }
291         }
292     }
293
294     @Override
295     public void checkPrereqs(List<MatchBuilder> matches) {
296         for (MatchBuilder match : matches) {
297             Long proto = null;
298             try {
299                 proto = Long.valueOf(match.getIpMatch().getIpProtocol().longValue());
300             } catch (NullPointerException e) {
301                 throw new IllegalArgumentException("Ip proto match is missing.");
302             }
303             if (!IpProtoClassifier.TCP.equals(proto) && !IpProtoClassifier.UDP.equals(proto)
304                     && !IpProtoClassifier.SCTP.equals(proto)) {
305                 throw new IllegalArgumentException("Unsupported proto value.\n" + "Classifier: "
306                         + this.getClass().getName() + ", proto set: " + proto);
307             }
308         }
309     }
310 }