1d72875725343b9b9c9b1e394e1f9a0ce292c486
[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.rev161219.ServiceFlowDirection;
20 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.TosByte;
21 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.ccap.attributes.AmId;
22 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.classifier.attributes.classifiers.ClassifierContainer;
23 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.classifier.attributes.classifiers.classifier.container.ClassifierChoice;
24 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.classifier.attributes.classifiers.classifier.container.classifier.choice.ExtClassifierChoice;
25 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.classifier.attributes.classifiers.classifier.container.classifier.choice.Ipv6ClassifierChoice;
26 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.classifier.attributes.classifiers.classifier.container.classifier.choice.QosClassifierChoice;
27 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.classifier.Classifier;
28 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.ext.classifier.ExtClassifier;
29 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.gate.spec.GateSpec;
30 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.ipv6.classifier.Ipv6Classifier;
31 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.traffic.profile.TrafficProfile;
32 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.serviceclass.name.profile.ServiceClassNameProfile;
33 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.flow.spec.profile.FlowSpecProfile;
34 import org.pcmm.gates.IClassifier;
35 import org.pcmm.gates.IClassifier.Protocol;
36 import org.pcmm.gates.IExtendedClassifier;
37 import org.pcmm.gates.IExtendedClassifier.ActivationState;
38 import org.pcmm.gates.IGateSpec.Direction;
39 import org.pcmm.gates.IIPv6Classifier.FlowLabel;
40 import org.pcmm.gates.ITrafficProfile;
41 import org.pcmm.gates.impl.AMID;
42 import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile;
43 import org.pcmm.gates.impl.DOCSISFlowSpecTrafficProfile;
44 import org.pcmm.gates.impl.GateID;
45 import org.pcmm.gates.impl.GateState;
46 import org.pcmm.gates.impl.GateTimeInfo;
47 import org.pcmm.gates.impl.GateUsageInfo;
48 import org.pcmm.gates.impl.PCMMError;
49 import org.pcmm.gates.impl.PCMMGateReq;
50 import org.pcmm.gates.impl.SessionClassID;
51 import org.pcmm.gates.impl.SubscriberID;
52 import org.pcmm.gates.impl.TransactionID;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.traffic.profile.TrafficProfile;
56 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.traffic.profile.traffic.profile.TrafficProfileChoice;
57 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.traffic.profile.traffic.profile.traffic.profile.choice.FlowSpecChoice;
58 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161219.pcmm.qos.traffic.profile.traffic.profile.traffic.profile.choice.ServiceClassNameChoice;
59  
60 /**
61  * Build PCMM gate requests from API QoS Gate objects
62  */
63 public class PCMMGateReqBuilder {
64
65     private final Logger logger = LoggerFactory.getLogger(PCMMGateReqBuilder.class);
66
67     private GateID gateID = null;
68     private AMID amid = null;
69     private SubscriberID subscriberID = null;
70     private TransactionID transactionID = null;
71     private org.pcmm.gates.impl.GateSpec gateSpec = null;
72     private ITrafficProfile trafficProfile = null;
73     private final List<IClassifier> classifiers = Lists.newArrayListWithExpectedSize(4);
74     private PCMMError error = null;
75     private GateState gateState = null;
76     private GateTimeInfo gateTimeInfo = null;
77     private GateUsageInfo gateUsageInfo = null;
78
79     public PCMMGateReq build() {
80         return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers,
81                 gateID, error, gateState, gateTimeInfo, gateUsageInfo);
82     }
83
84     public void setAmId(final AmId qosAmId) {
85         amid = new AMID(qosAmId.getAmType().shortValue(), qosAmId.getAmTag().shortValue());
86     }
87
88     public void setSubscriberId(final InetAddress qosSubId) {
89         subscriberID = new SubscriberID(qosSubId);
90     }
91
92     public void setGateSpec(final GateSpec qosGateSpec, final ServiceFlowDirection scnDirection) {
93
94         final ServiceFlowDirection qosDir;
95         if (scnDirection != null) {
96             qosDir = scnDirection;
97         } else {
98             if (qosGateSpec.getDirection() != null) {
99                 qosDir = qosGateSpec.getDirection();
100             } else {
101                 // TODO - determine if this is a valid default value
102                 qosDir = ServiceFlowDirection.Ds;
103             }
104         }
105
106         final Direction gateDir;
107         if (qosDir == ServiceFlowDirection.Ds) {
108             gateDir = Direction.DOWNSTREAM;
109         } else {
110             gateDir = Direction.UPSTREAM;
111         }
112
113         // DSCP/TOS Overwrite
114         final byte dscptos;
115         final byte gateTosMask;
116
117         final TosByte tosOverwrite = qosGateSpec.getDscpTosOverwrite();
118         if (tosOverwrite != null) {
119             dscptos = 1;
120             TosByte tosMask = qosGateSpec.getDscpTosMask();
121             if (tosMask != null) {
122                 gateTosMask = tosMask.getValue().byteValue();
123             } else {
124                 gateTosMask = (byte) 0xff;
125             }
126         } else {
127             // TODO - These values appear to be required
128             dscptos = 0;
129             gateTosMask = 0;
130         }
131
132         byte sessionClassId = 0;
133         if (qosGateSpec.getSessionClassId() != null) {
134            sessionClassId = (byte)(qosGateSpec.getSessionClassId() & 255);
135         }
136
137         short inactivityTimer = 300;
138         if (qosGateSpec.getInactivityTimer() != null) {
139             inactivityTimer = (short)(qosGateSpec.getInactivityTimer() & 65535);
140         }
141
142         gateSpec = new org.pcmm.gates.impl.GateSpec(gateDir, dscptos, gateTosMask,
143                                                     new SessionClassID(sessionClassId),
144                                                     (short)1, (short)300, inactivityTimer, (short)0);
145    }
146
147     public void setTrafficProfile(final TrafficProfile qosTrafficProfile) {
148         TrafficProfileChoice choice = qosTrafficProfile.getTrafficProfileChoice();
149
150         if (choice instanceof ServiceClassNameChoice) {
151             ServiceClassNameProfile scnp = ((ServiceClassNameChoice)choice).getServiceClassNameProfile();
152             trafficProfile = new DOCSISServiceClassNameTrafficProfile(scnp.getServiceClassName().getValue());
153         }
154         else if (choice instanceof FlowSpecChoice) {
155             FlowSpecProfile fsp = ((FlowSpecChoice)choice).getFlowSpecProfile();
156             trafficProfile = new DOCSISFlowSpecTrafficProfile(fsp.getTokenBucketRate(),
157                                                               fsp.getTokenBucketSize(),
158                                                               fsp.getPeakDataRate(),
159                                                               fsp.getMinimumPolicedUnit(),
160                                                               fsp.getMaximumPacketSize(),
161                                                               fsp.getRate(),
162                                                               fsp.getSlackTerm());     
163         }
164         else {
165             logger.debug("PCMMGateReq().setTrafficProfile() Unsupported Traffic Profile: " + choice.getClass().getName());
166         }
167     }
168
169     private InetAddress getByName(final String ipAddressStr) {
170         try {
171             return InetAddress.getByName(ipAddressStr);
172         } catch (UnknownHostException e) {
173             logger.error(e.getMessage());
174         }
175         return null;
176     }
177
178     public void setClassifiers(final List<ClassifierContainer> classifiers) {
179         checkNotNull(classifiers);
180
181         for (ClassifierContainer container : classifiers) {
182             final ClassifierChoice choice = container.getClassifierChoice();
183             final Short index = container.getClassifierId();
184
185             if (choice instanceof QosClassifierChoice) {
186                 addClassifier(index, ((QosClassifierChoice) choice).getClassifier());
187             }
188             else if (choice instanceof ExtClassifierChoice) {
189                 addExtClassifier(index, ((ExtClassifierChoice) choice).getExtClassifier());
190             }
191             else if (choice instanceof Ipv6ClassifierChoice) {
192                 addIpv6Classifier(index, ((Ipv6ClassifierChoice) choice).getIpv6Classifier());
193             }
194             else {
195                 throw new IllegalStateException("Unknown ClassifierChoice: " + choice);
196             }
197         }
198     }
199
200     private void addClassifier(final Short index,final Classifier qosClassifier) {
201         // TODO - try and make these variables immutable
202         Protocol protocol = null;
203         byte tosOverwrite = 0;
204         byte tosMask = (byte)0x0;
205         short srcPort = (short) 0;
206         short dstPort = (short) 0;
207
208         // Legacy classifier
209
210         // Protocol -- zero is match any
211         if (qosClassifier.getProtocol() != null) {
212             protocol = Protocol.valueOf(qosClassifier.getProtocol().getValue().shortValue());
213         } else {
214             protocol = Protocol.NONE;
215         }
216
217         // IP Addresss and mask wildcards - addr byte 0 for no match (or match anything)
218
219         Inet4Address srcAddress = (Inet4Address) getByName("0.0.0.0");
220
221         if (qosClassifier.getSrcIp() != null) {
222             srcAddress = (Inet4Address) getByName(qosClassifier.getSrcIp().getValue());
223         }
224
225         Inet4Address dstAddress = (Inet4Address) getByName("0.0.0.0");
226
227         if (qosClassifier.getDstIp() != null) {
228             dstAddress = (Inet4Address) getByName(qosClassifier.getDstIp().getValue());
229         }
230
231
232         if (qosClassifier.getSrcPort() != null) {
233             srcPort = qosClassifier.getSrcPort().getValue().shortValue();
234         }
235         if (qosClassifier.getDstPort() != null) {
236             dstPort = qosClassifier.getDstPort().getValue().shortValue();
237         }
238         if (qosClassifier.getTosByte() != null) {
239             tosOverwrite = qosClassifier.getTosByte().getValue().byteValue();
240             if (qosClassifier.getTosMask() != null) {
241                 tosMask = qosClassifier.getTosMask().getValue().byteValue();
242             } else {
243                 // set default TOS mask
244                 tosMask = (byte) 0xff;
245             }
246         }
247         //
248         // The packetcable.yang models priority as an uint8 which means the java generated
249         // implementation saves the value in a short, so we mask it back into a byte
250         //
251         byte priority = 64;
252         if (qosClassifier.getPriority() != null) {
253             short result = qosClassifier.getPriority();
254             priority = (byte)(result % 255);
255         }
256
257         // push the classifier to the gate request
258         classifiers.add(new org.pcmm.gates.impl.Classifier(protocol, tosOverwrite, tosMask, srcAddress, dstAddress, srcPort,
259                 dstPort, priority));
260     }
261
262     private void addExtClassifier(final Short index, final ExtClassifier qosExtClassifier) {
263         // Extended classifier
264         // Protocol -- zero is match any
265         final Protocol protocol;
266         if (qosExtClassifier.getProtocol() != null) {
267             protocol = Protocol.valueOf(qosExtClassifier.getProtocol().getValue().shortValue());
268         } else {
269             protocol = Protocol.NONE;
270         }
271
272         // default source port range must be set to match any even if qosExtClassifier has no range
273         // match any port range is 0-65535, NOT 0-0
274         // TODO - try to make these two variables immutable
275         short srcStartPort = (short) 0;
276         short srcEndPort = (short) 65535;
277         if (qosExtClassifier.getSrcPortStart() != null) {
278             srcStartPort = qosExtClassifier.getSrcPortStart().getValue().shortValue();
279             srcEndPort = srcStartPort;
280             if (qosExtClassifier.getSrcPortEnd() != null) {
281                 srcEndPort = qosExtClassifier.getSrcPortEnd().getValue().shortValue();
282             }
283             if ((int)(srcStartPort & 0xffff) > (int) (srcEndPort & 0xffff)) {
284                 logger.warn("Start port %d > End port %d in ext-classifier source port range -- forcing to same",
285                         srcStartPort, srcEndPort);
286                 srcEndPort = srcStartPort;
287             }
288         }
289         // default destination port range must be set to match any even if qosExtClassifier has no range
290         // match any port range is 0-65535, NOT 0-0
291         // TODO - try to make these two variables immutable
292         short dstStartPort = (short) 0;
293         short dstEndPort = (short) 65535;
294         if (qosExtClassifier.getDstPortStart() != null) {
295             dstStartPort = qosExtClassifier.getDstPortStart().getValue().shortValue();
296             dstEndPort = dstStartPort;
297             if (qosExtClassifier.getDstPortEnd() != null) {
298                 dstEndPort = qosExtClassifier.getDstPortEnd().getValue().shortValue();
299             }
300             if ((int)(dstStartPort & 0xffff) > (int)(dstEndPort & 0xffff)) {
301                 logger.warn("Start port %d > End port %d in ext-classifier destination port range -- forcing to same",
302                         dstStartPort, dstEndPort);
303                 dstEndPort = dstStartPort;
304             }
305         }
306
307         // DSCP/TOP byte
308         // TODO - try to make these two variables immutable
309         byte tosOverwrite = 0;
310         byte tosMask = (byte)0x00;
311         if (qosExtClassifier.getTosByte() != null) {
312             // OR in the DSCP/TOS enable bit 0x01
313             tosOverwrite = (byte) (qosExtClassifier.getTosByte().getValue().byteValue() | 0x01);
314             if (qosExtClassifier.getTosMask() != null) {
315                 tosMask = qosExtClassifier.getTosMask().getValue().byteValue();
316             } else {
317                 // set default TOS mask
318                 tosMask = (byte) 0xff;
319             }
320         }
321
322         // IP Addresss and mask wildcards - addr byte 0 for no match (or match anything) and mask is 255.255.255.255 by default
323         Inet4Address srcIpAddr = (Inet4Address) getByName("0.0.0.0");
324
325         if (qosExtClassifier.getSrcIp() != null) {
326             srcIpAddr = getInet4Address(qosExtClassifier.getSrcIp());
327         }
328
329         Inet4Address dstIpAddr = (Inet4Address) getByName("0.0.0.0");
330         if (qosExtClassifier.getDstIp() != null) {
331             dstIpAddr = getInet4Address(qosExtClassifier.getDstIp());
332         }
333
334         //mask
335         Inet4Address srcIpMask = (Inet4Address) getByName("255.255.255.255");
336         if (qosExtClassifier.getSrcIpMask() != null) {
337             srcIpMask = getInet4Address(qosExtClassifier.getSrcIpMask());
338         }
339
340         Inet4Address dstIpMask = (Inet4Address) getByName("255.255.255.255");
341         if (qosExtClassifier.getDstIpMask() != null) {
342             dstIpMask = getInet4Address(qosExtClassifier.getDstIpMask());
343         }
344
345         // TODO - find out what the classifier ID should really be. It was never getting set previously
346         final short classifierId = (short)index;
347
348         // TODO - find out what the action value should really be. It was never getting set previously
349         final IExtendedClassifier.Action action = IExtendedClassifier.Action.ADD;
350
351         //
352         // Convert from Enum to byte for serialization
353         //
354         ActivationState activationState = ActivationState.ACTIVE;
355         if (qosExtClassifier.getActivationState() != null) {
356             activationState = ActivationState.valueOf((byte)qosExtClassifier.getActivationState().getIntValue());
357         }
358
359         //
360         // The packetcable.yang models priority as an uint8 which means the java generated
361         // implementation saves the value in a short, so we mask it back into a byte
362         //
363         byte priority = 64;
364         if (qosExtClassifier.getPriority() != null) {
365             short result = qosExtClassifier.getPriority();
366             priority = (byte)(result % 255);
367         }
368
369         // push the extended classifier to the gate request
370         classifiers.add(new org.pcmm.gates.impl.ExtendedClassifier(protocol, tosOverwrite, tosMask,
371                 srcIpAddr, dstIpAddr,
372                 srcStartPort, dstStartPort, priority, srcIpMask, dstIpMask, srcEndPort, dstEndPort, classifierId, activationState,
373                 action));
374     }
375
376     private Inet4Address getInet4Address(
377             final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address address) {
378         if (address != null) {
379             final InetAddress out = getByName(address.getValue());
380             if (out != null && out instanceof Inet4Address) {
381                 return (Inet4Address) out;
382             }
383         }
384         return null;
385     }
386
387     private void addIpv6Classifier(final Short index, final Ipv6Classifier qosIpv6Classifier) {
388         // Next Header
389         final short nextHdr;
390         if (qosIpv6Classifier.getNextHdr() != null) {
391             nextHdr = qosIpv6Classifier.getNextHdr().getValue().shortValue();
392         }
393         // default: match any nextHdr is 256 because nextHdr 0 is Hop-by-Hop option
394         else {
395             nextHdr = (short) 256;
396         }
397
398         // Source IPv6 address & prefix len
399         // TODO - try to make these two variables immutable
400         byte srcPrefixLen = (byte) 128;
401         Inet6Address srcAddress = (Inet6Address) getByName("0::0");
402
403         if (qosIpv6Classifier.getSrcIp6() != null) {
404             String[] parts = qosIpv6Classifier.getSrcIp6().getValue().split("/");
405             String Ipv6AddressStr = parts[0];
406             srcAddress = (Inet6Address) getByName(Ipv6AddressStr);
407             if (parts.length > 1) {
408                 srcPrefixLen = (byte) Integer.parseInt(parts[1]);
409             } else {
410                 srcPrefixLen = (byte) 128;
411             }
412
413         }
414
415         // TODO - try to make these two variables immutable
416         Inet6Address dstAddress = (Inet6Address) getByName("0::0");
417
418         byte dstPrefLen = (byte) 128;
419         // Destination IPv6 address & prefix len
420         if (qosIpv6Classifier.getDstIp6() != null) {
421             final String[] parts = qosIpv6Classifier.getDstIp6().getValue().split("/");
422             final String Ipv6AddressStr = parts[0];
423             dstAddress = (Inet6Address)getByName(Ipv6AddressStr);
424             if (parts.length > 1) dstPrefLen = (byte) Integer.parseInt(parts[1]);
425             else dstPrefLen = (byte) 128;
426         }
427
428         // default source port range must be set to match any -- even if qosExtClassifier has no range value
429         // match any port range is 0-65535, NOT 0-0
430         short srcPortBegin = (short) 0;
431         short srcPortEnd = (short) 65535;
432         if (qosIpv6Classifier.getSrcPortStart() != null) {
433             srcPortBegin = qosIpv6Classifier.getSrcPortStart().getValue().shortValue();
434             srcPortEnd = srcPortBegin;
435             if (qosIpv6Classifier.getSrcPortEnd() != null) {
436                 srcPortEnd = qosIpv6Classifier.getSrcPortEnd().getValue().shortValue();
437             }
438             if ((int)(srcPortBegin & 0xffff) > (int)(srcPortEnd & 0xffff)) {
439                 logger.warn("Start port %d > End port %d in ipv6-classifier source port range -- forcing to same",
440                         srcPortBegin, srcPortEnd);
441                 srcPortEnd = srcPortBegin;
442             }
443         }
444
445         // default destination port range must be set to match any -- even if qosExtClassifier has no range value
446         // match any port range is 0-65535, NOT 0-0
447         short dstPortBegin = (short) 0;
448         short dstPortEnd = (short) 65535;
449         if (qosIpv6Classifier.getDstPortStart() != null) {
450             dstPortBegin = qosIpv6Classifier.getDstPortStart().getValue().shortValue();
451             dstPortEnd = dstPortBegin;
452             if (qosIpv6Classifier.getDstPortEnd() != null) {
453                 dstPortEnd = qosIpv6Classifier.getDstPortEnd().getValue().shortValue();
454             }
455             if ( (int)(dstPortBegin & 0xffff) > (int)(dstPortEnd & 0xffff)) {
456                 logger.warn("Start port %d > End port %d in ipv6-classifier destination port range -- forcing to same",
457                         dstPortBegin, dstPortEnd);
458                 dstPortEnd = dstPortBegin;
459             }
460         }
461
462         final byte tcLow;
463         if (qosIpv6Classifier.getTcLow() != null)
464             tcLow = qosIpv6Classifier.getTcLow().getValue().byteValue();
465         else tcLow = (byte) 0x00;
466
467         final byte tcHigh;
468         if (qosIpv6Classifier.getTcHigh() != null)
469             tcHigh = qosIpv6Classifier.getTcHigh().getValue().byteValue();
470         else tcHigh = (byte) 0x00;
471
472         final byte tcMask;
473         if (qosIpv6Classifier.getTcHigh() != null)
474             tcMask = qosIpv6Classifier.getTcHigh().getValue().byteValue();
475         else if (qosIpv6Classifier.getTcLow() != null) tcMask = (byte) 0xff;
476         else tcMask = (byte) 0x00;
477
478         FlowLabel flowLabelFlag = FlowLabel.IRRELEVANT;
479         int flowLabelId = 0;
480
481         if (qosIpv6Classifier.getFlowLabel() != null) {
482             flowLabelFlag = FlowLabel.VALID;
483             flowLabelId = qosIpv6Classifier.getFlowLabel().intValue();
484         }
485
486
487         // TODO - find out what the classifier ID should really be. It was never getting set previously
488         final short classifierId = (short)index;
489
490         // TODO - find out what the action value should really be. It was never getting set previously
491         final IExtendedClassifier.Action action = IExtendedClassifier.Action.ADD;
492
493         //
494         // Convert from Enum to byte for serialization
495         //
496         ActivationState activationState = ActivationState.ACTIVE;
497         if (qosIpv6Classifier.getActivationState() != null) {
498             activationState = ActivationState.valueOf((byte)qosIpv6Classifier.getActivationState().getIntValue());
499         }
500
501         //
502         // The packetcable.yang models priority as an uint8 which means the java generated
503         // implementation saves the value in a short, so we mask it back into a byte
504         //
505         byte priority = 64;
506         if (qosIpv6Classifier.getPriority() != null) {
507             short result = qosIpv6Classifier.getPriority();
508             priority = (byte)(result % 255);
509         }
510
511         // push the IPv6 classifier to the gate request
512         classifiers.add(
513                 new org.pcmm.gates.impl.IPv6Classifier(srcAddress, dstAddress, srcPortBegin, dstPortBegin, priority,
514                         srcPortEnd, dstPortEnd, classifierId, activationState, action, flowLabelFlag, tcLow,
515                         tcHigh, tcMask, flowLabelId, nextHdr, srcPrefixLen, dstPrefLen));
516     }
517 }