Adding support for multiple classifiers per gate
[packetcable.git] / packetcable-policy-server / src / main / java / org / opendaylight / controller / packetcable / provider / PCMMGateReqBuilder.java
1 /*
2  * Copyright (c) 2015 CableLabs 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.controller.packetcable.provider;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import com.google.common.collect.Lists;
14 import java.net.Inet4Address;
15 import java.net.Inet6Address;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import java.util.List;
19 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection;
20 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.TosByte;
21 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.AmId;
22 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.ClassifierContainer;
23 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.ClassifierChoice;
24 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.classifier.choice.ExtClassifierChoice;
25 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.classifier.choice.Ipv6ClassifierChoice;
26 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.classifier.choice.QosClassifierChoice;
27 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.classifier.Classifier;
28 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.ext.classifier.ExtClassifier;
29 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gate.spec.GateSpec;
30 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.ipv6.classifier.Ipv6Classifier;
31 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.traffic.profile.TrafficProfile;
32 import org.pcmm.gates.IClassifier;
33 import org.pcmm.gates.IClassifier.Protocol;
34 import org.pcmm.gates.IExtendedClassifier;
35 import org.pcmm.gates.IExtendedClassifier.ActivationState;
36 import org.pcmm.gates.IGateSpec.Direction;
37 import org.pcmm.gates.IIPv6Classifier.FlowLabel;
38 import org.pcmm.gates.ITrafficProfile;
39 import org.pcmm.gates.impl.AMID;
40 import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile;
41 import org.pcmm.gates.impl.GateID;
42 import org.pcmm.gates.impl.PCMMError;
43 import org.pcmm.gates.impl.PCMMGateReq;
44 import org.pcmm.gates.impl.SubscriberID;
45 import org.pcmm.gates.impl.TransactionID;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * Build PCMM gate requests from API QoS Gate objects
51  */
52 public class PCMMGateReqBuilder {
53
54     private final Logger logger = LoggerFactory.getLogger(PCMMGateReqBuilder.class);
55
56     private GateID gateID = null;
57     private AMID amid = null;
58     private SubscriberID subscriberID = null;
59     private TransactionID transactionID = null;
60     private org.pcmm.gates.impl.GateSpec gateSpec = null;
61     private ITrafficProfile trafficProfile = null;
62     private final List<IClassifier> classifiers = Lists.newArrayListWithExpectedSize(4);
63     private PCMMError error = null;
64
65     public PCMMGateReq build() {
66         return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers, gateID, error);
67     }
68
69     public void setAmId(final AmId qosAmId) {
70         amid = new AMID(qosAmId.getAmType().shortValue(), qosAmId.getAmTag().shortValue());
71     }
72
73     public void setSubscriberId(final InetAddress qosSubId) {
74         subscriberID = new SubscriberID(qosSubId);
75     }
76
77     public void setGateSpec(final GateSpec qosGateSpec, final ServiceFlowDirection scnDirection) {
78
79         final ServiceFlowDirection qosDir;
80         if (scnDirection != null) {
81             qosDir = scnDirection;
82         } else {
83             if (qosGateSpec.getDirection() != null) {
84                 qosDir = qosGateSpec.getDirection();
85             } else {
86                 // TODO - determine if this is a valid default value
87                 qosDir = ServiceFlowDirection.Ds;
88             }
89         }
90
91         final Direction gateDir;
92         if (qosDir == ServiceFlowDirection.Ds) {
93             gateDir = Direction.DOWNSTREAM;
94         } else {
95             gateDir = Direction.UPSTREAM;
96         }
97
98         // DSCP/TOS Overwrite
99         final byte dscptos;
100         final byte gateTosMask;
101
102         final TosByte tosOverwrite = qosGateSpec.getDscpTosOverwrite();
103         if (tosOverwrite != null) {
104             dscptos = 1;
105             TosByte tosMask = qosGateSpec.getDscpTosMask();
106             if (tosMask != null) {
107                 gateTosMask = tosMask.getValue().byteValue();
108             } else {
109                 gateTosMask = (byte) 0xff;
110             }
111         } else {
112             // TODO - These values appear to be required
113             dscptos = 0;
114             gateTosMask = 0;
115         }
116         gateSpec = new org.pcmm.gates.impl.GateSpec(gateDir, dscptos, gateTosMask);
117     }
118
119     public void setTrafficProfile(final TrafficProfile qosTrafficProfile) {
120         if (qosTrafficProfile.getServiceClassName() != null) {
121             trafficProfile =
122                     new DOCSISServiceClassNameTrafficProfile(qosTrafficProfile.getServiceClassName().getValue());
123         }
124     }
125
126     private InetAddress getByName(final String ipAddressStr) {
127         try {
128             return InetAddress.getByName(ipAddressStr);
129         } catch (UnknownHostException e) {
130             logger.error(e.getMessage());
131         }
132         return null;
133     }
134
135     public void setClassifiers(final List<ClassifierContainer> classifiers) {
136         checkNotNull(classifiers);
137
138         for (ClassifierContainer container : classifiers) {
139             final ClassifierChoice choice = container.getClassifierChoice();
140             final Short index = container.getClassifierId();
141
142             if (choice instanceof QosClassifierChoice) {
143                 addClassifier(((QosClassifierChoice) choice).getClassifier());
144             }
145             else if (choice instanceof ExtClassifierChoice) {
146                 addExtClassifier(index, ((ExtClassifierChoice) choice).getExtClassifier());
147             }
148             else if (choice instanceof Ipv6ClassifierChoice) {
149                 addIpv6Classifier(index, ((Ipv6ClassifierChoice) choice).getIpv6Classifier());
150             }
151             else {
152                 throw new IllegalStateException("Unknown ClassifierChoice: " + choice);
153             }
154         }
155     }
156
157     private void addClassifier(final Classifier qosClassifier) {
158         // TODO - try and make these variables immutable
159         Protocol protocol = null;
160         byte tosOverwrite = 0;
161         byte tosMask = (byte)0x0;
162         Inet4Address srcAddress = null;
163         Inet4Address dstAddress = null;
164         short srcPort = (short) 0;
165         short dstPort = (short) 0;
166         byte priority = (byte) 64;
167
168         // Legacy classifier
169         if (qosClassifier.getProtocol() != null) {
170             protocol = Protocol.valueOf(qosClassifier.getProtocol().getValue().shortValue());
171         }
172         if (qosClassifier.getSrcIp() != null) {
173             final InetAddress sip = getByName(qosClassifier.getSrcIp().getValue());
174             if (sip != null && sip instanceof Inet4Address) {
175                 srcAddress = (Inet4Address) sip;
176             }
177         }
178         if (qosClassifier.getDstIp() != null) {
179             final InetAddress dip = getByName(qosClassifier.getDstIp().getValue());
180             if (dip != null && dip instanceof Inet4Address) {
181                 dstAddress = (Inet4Address) dip;
182             }
183         }
184         if (qosClassifier.getSrcPort() != null) {
185             srcPort = qosClassifier.getSrcPort().getValue().shortValue();
186         }
187         if (qosClassifier.getDstPort() != null) {
188             dstPort = qosClassifier.getDstPort().getValue().shortValue();
189         }
190         if (qosClassifier.getTosByte() != null) {
191             tosOverwrite = qosClassifier.getTosByte().getValue().byteValue();
192             if (qosClassifier.getTosMask() != null) {
193                 tosMask = qosClassifier.getTosMask().getValue().byteValue();
194             } else {
195                 // set default TOS mask
196                 tosMask = (byte) 0xff;
197             }
198         }
199         // push the classifier to the gate request
200         classifiers.add(new org.pcmm.gates.impl.Classifier(protocol, tosOverwrite, tosMask, srcAddress, dstAddress, srcPort,
201                         dstPort, priority));
202     }
203
204     private void addExtClassifier(final Short index, final ExtClassifier qosExtClassifier) {
205         // Extended classifier
206         final byte priority = (byte) 64;
207         final ActivationState activationState = ActivationState.ACTIVE;
208         // Protocol -- zero is match any
209         final Protocol protocol;
210         if (qosExtClassifier.getProtocol() != null) {
211             protocol = Protocol.valueOf(qosExtClassifier.getProtocol().getValue().shortValue());
212         } else {
213             protocol = Protocol.NONE;
214         }
215
216         // default source port range must be set to match any even if qosExtClassifier has no range
217         // match any port range is 0-65535, NOT 0-0
218         // TODO - try to make these two variables immutable
219         short srcStartPort = (short) 0;
220         short srcEndPort = (short) 65535;
221         if (qosExtClassifier.getSrcPortStart() != null) {
222             srcStartPort = qosExtClassifier.getSrcPortStart().getValue().shortValue();
223             srcEndPort = srcStartPort;
224             if (qosExtClassifier.getSrcPortEnd() != null) {
225                 srcEndPort = qosExtClassifier.getSrcPortEnd().getValue().shortValue();
226             }
227             if (srcStartPort > srcEndPort) {
228                 logger.warn("Start port %d > End port %d in ext-classifier source port range -- forcing to same",
229                         srcStartPort, srcEndPort);
230                 srcEndPort = srcStartPort;
231             }
232         }
233         // default destination port range must be set to match any even if qosExtClassifier has no range
234         // match any port range is 0-65535, NOT 0-0
235         // TODO - try to make these two variables immutable
236         short dstStartPort = (short) 0;
237         short dstEndPort = (short) 65535;
238         if (qosExtClassifier.getDstPortStart() != null) {
239             dstStartPort = qosExtClassifier.getDstPortStart().getValue().shortValue();
240             dstEndPort = dstStartPort;
241             if (qosExtClassifier.getDstPortEnd() != null) {
242                 dstEndPort = qosExtClassifier.getDstPortEnd().getValue().shortValue();
243             }
244             if (dstStartPort > dstEndPort) {
245                 logger.warn("Start port %d > End port %d in ext-classifier destination port range -- forcing to same",
246                         dstStartPort, dstEndPort);
247                 dstEndPort = dstStartPort;
248             }
249         }
250
251         // DSCP/TOP byte
252         // TODO - try to make these two variables immutable
253         byte tosOverwrite = 0;
254         byte tosMask = (byte)0x00;
255         if (qosExtClassifier.getTosByte() != null) {
256             // OR in the DSCP/TOS enable bit 0x01
257             tosOverwrite = (byte) (qosExtClassifier.getTosByte().getValue().byteValue() | 0x01);
258             if (qosExtClassifier.getTosMask() != null) {
259                 tosMask = qosExtClassifier.getTosMask().getValue().byteValue();
260             } else {
261                 // set default TOS mask
262                 tosMask = (byte) 0xff;
263             }
264         }
265
266         // TODO - find out what the classifier ID should really be. It was never getting set previously
267         final short classifierId = (short)index;
268
269         // TODO - find out what the action value should really be. It was never getting set previously
270         final IExtendedClassifier.Action action = IExtendedClassifier.Action.ADD;
271
272         // push the extended classifier to the gate request
273         classifiers.add(new org.pcmm.gates.impl.ExtendedClassifier(protocol, tosOverwrite, tosMask,
274                 getInet4Address(qosExtClassifier.getSrcIp()), getInet4Address(qosExtClassifier.getDstIp()),
275                 srcStartPort, dstStartPort, priority, getInet4Address(qosExtClassifier.getSrcIpMask()),
276                 getInet4Address(qosExtClassifier.getDstIpMask()), srcEndPort, dstEndPort, classifierId, activationState,
277                 action));
278     }
279
280     private Inet4Address getInet4Address(
281             final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address address) {
282         if (address != null) {
283             final InetAddress out = getByName(address.getValue());
284             if (out != null && out instanceof Inet4Address) {
285                 return (Inet4Address) out;
286             }
287         }
288         return null;
289     }
290
291     private void addIpv6Classifier(final Short index, final Ipv6Classifier qosIpv6Classifier) {
292         // Next Header
293         final short nextHdr;
294         if (qosIpv6Classifier.getNextHdr() != null) {
295             nextHdr = qosIpv6Classifier.getNextHdr().getValue().shortValue();
296         }
297         // default: match any nextHdr is 256 because nextHdr 0 is Hop-by-Hop option
298         else {
299             nextHdr = (short) 256;
300         }
301
302         // Source IPv6 address & prefix len
303         // TODO - try to make these two variables immutable
304         byte srcPrefixLen = (byte) 128;
305         Inet6Address srcAddress = null;
306         if (qosIpv6Classifier.getSrcIp6() != null) {
307             String[] parts = qosIpv6Classifier.getSrcIp6().getValue().split("/");
308             String Ipv6AddressStr = parts[0];
309             srcAddress = (Inet6Address) getByName(Ipv6AddressStr);
310             if (parts.length > 1) {
311                 srcPrefixLen = (byte) Integer.parseInt(parts[1]);
312             } else {
313                 srcPrefixLen = (byte) 128;
314             }
315
316         }
317
318         // TODO - try to make these two variables immutable
319         Inet6Address dstAddress = null;
320         byte dstPrefLen = (byte) 128;
321         // Destination IPv6 address & prefix len
322         if (qosIpv6Classifier.getDstIp6() != null) {
323             final String[] parts = qosIpv6Classifier.getDstIp6().getValue().split("/");
324             final String Ipv6AddressStr = parts[0];
325             dstAddress = (Inet6Address)getByName(Ipv6AddressStr);
326             if (parts.length > 1) dstPrefLen = (byte) Integer.parseInt(parts[1]);
327             else dstPrefLen = (byte) 128;
328         }
329
330         // default source port range must be set to match any -- even if qosExtClassifier has no range value
331         // match any port range is 0-65535, NOT 0-0
332         short srcPortBegin = (short) 0;
333         short srcPortEnd = (short) 65535;
334         if (qosIpv6Classifier.getSrcPortStart() != null) {
335             srcPortBegin = qosIpv6Classifier.getSrcPortStart().getValue().shortValue();
336             srcPortEnd = srcPortBegin;
337             if (qosIpv6Classifier.getSrcPortEnd() != null) {
338                 srcPortEnd = qosIpv6Classifier.getSrcPortEnd().getValue().shortValue();
339             }
340             if (srcPortBegin > srcPortEnd) {
341                 logger.warn("Start port %d > End port %d in ipv6-classifier source port range -- forcing to same",
342                         srcPortBegin, srcPortEnd);
343                 srcPortEnd = srcPortBegin;
344             }
345         }
346
347         // default destination port range must be set to match any -- even if qosExtClassifier has no range value
348         // match any port range is 0-65535, NOT 0-0
349         short dstPortBegin = (short) 0;
350         short dstPortEnd = (short) 65535;
351         if (qosIpv6Classifier.getDstPortStart() != null) {
352             dstPortBegin = qosIpv6Classifier.getDstPortStart().getValue().shortValue();
353             dstPortEnd = dstPortBegin;
354             if (qosIpv6Classifier.getDstPortEnd() != null) {
355                 dstPortEnd = qosIpv6Classifier.getDstPortEnd().getValue().shortValue();
356             }
357             if (dstPortBegin > dstPortEnd) {
358                 logger.warn("Start port %d > End port %d in ipv6-classifier destination port range -- forcing to same",
359                         dstPortBegin, dstPortEnd);
360                 dstPortEnd = dstPortBegin;
361             }
362         }
363
364         final byte tcLow;
365         if (qosIpv6Classifier.getTcLow() != null)
366             tcLow = qosIpv6Classifier.getTcLow().getValue().byteValue();
367         else tcLow = (byte) 0x00;
368
369         final byte tcHigh;
370         if (qosIpv6Classifier.getTcHigh() != null)
371             tcHigh = qosIpv6Classifier.getTcHigh().getValue().byteValue();
372         else tcHigh = (byte) 0x00;
373
374         final byte tcMask;
375         if (qosIpv6Classifier.getTcHigh() != null)
376             tcMask = qosIpv6Classifier.getTcHigh().getValue().byteValue();
377         else if (qosIpv6Classifier.getTcLow() != null) tcMask = (byte) 0xff;
378         else tcMask = (byte) 0x00;
379
380         // TODO - find out what the classifier ID should really be. It was never getting set previously
381         final short classifierId = (short)index;
382
383         // TODO - find out what the action value should really be. It was never getting set previously
384         final IExtendedClassifier.Action action = IExtendedClassifier.Action.ADD;
385
386         // push the IPv6 classifier to the gate request
387         classifiers.add(
388                 new org.pcmm.gates.impl.IPv6Classifier(srcAddress, dstAddress, srcPortBegin, dstPortBegin, (byte) 64,
389                         srcPortEnd, dstPortEnd, classifierId, ActivationState.ACTIVE, action, FlowLabel.VALID, tcLow,
390                         tcHigh, tcMask, qosIpv6Classifier.getFlowLabel().intValue(), nextHdr, srcPrefixLen, dstPrefLen));
391     }
392 }