2 * Copyright (c) 2015 CableLabs and others. All rights reserved.
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
9 package org.opendaylight.controller.packetcable.provider;
11 import static com.google.common.base.Preconditions.checkNotNull;
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;
61 * Build PCMM gate requests from API QoS Gate objects
63 public class PCMMGateReqBuilder {
65 private final Logger logger = LoggerFactory.getLogger(PCMMGateReqBuilder.class);
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;
79 public PCMMGateReq build() {
80 return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers,
81 gateID, error, gateState, gateTimeInfo, gateUsageInfo);
84 public void setAmId(final AmId qosAmId) {
85 amid = new AMID(qosAmId.getAmType().shortValue(), qosAmId.getAmTag().shortValue());
88 public void setSubscriberId(final InetAddress qosSubId) {
89 subscriberID = new SubscriberID(qosSubId);
92 public void setGateSpec(final GateSpec qosGateSpec, final ServiceFlowDirection scnDirection) {
94 final ServiceFlowDirection qosDir;
95 if (scnDirection != null) {
96 qosDir = scnDirection;
98 if (qosGateSpec.getDirection() != null) {
99 qosDir = qosGateSpec.getDirection();
101 // TODO - determine if this is a valid default value
102 qosDir = ServiceFlowDirection.Ds;
106 final Direction gateDir;
107 if (qosDir == ServiceFlowDirection.Ds) {
108 gateDir = Direction.DOWNSTREAM;
110 gateDir = Direction.UPSTREAM;
113 // DSCP/TOS Overwrite
115 final byte gateTosMask;
117 final TosByte tosOverwrite = qosGateSpec.getDscpTosOverwrite();
118 if (tosOverwrite != null) {
120 TosByte tosMask = qosGateSpec.getDscpTosMask();
121 if (tosMask != null) {
122 gateTosMask = tosMask.getValue().byteValue();
124 gateTosMask = (byte) 0xff;
127 // TODO - These values appear to be required
132 byte sessionClassId = 0;
133 if (qosGateSpec.getSessionClassId() != null) {
134 sessionClassId = (byte)(qosGateSpec.getSessionClassId() & 255);
137 short inactivityTimer = 300;
138 if (qosGateSpec.getInactivityTimer() != null) {
139 inactivityTimer = (short)(qosGateSpec.getInactivityTimer() & 65535);
142 gateSpec = new org.pcmm.gates.impl.GateSpec(gateDir, dscptos, gateTosMask,
143 new SessionClassID(sessionClassId),
144 (short)1, (short)300, inactivityTimer, (short)0);
147 public void setTrafficProfile(final TrafficProfile qosTrafficProfile) {
148 TrafficProfileChoice choice = qosTrafficProfile.getTrafficProfileChoice();
150 if (choice instanceof ServiceClassNameChoice) {
151 ServiceClassNameProfile scnp = ((ServiceClassNameChoice)choice).getServiceClassNameProfile();
152 trafficProfile = new DOCSISServiceClassNameTrafficProfile(scnp.getServiceClassName().getValue());
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(),
165 logger.debug("PCMMGateReq().setTrafficProfile() Unsupported Traffic Profile: " + choice.getClass().getName());
169 private InetAddress getByName(final String ipAddressStr) {
171 return InetAddress.getByName(ipAddressStr);
172 } catch (UnknownHostException e) {
173 logger.error(e.getMessage());
178 public void setClassifiers(final List<ClassifierContainer> classifiers) {
179 checkNotNull(classifiers);
181 for (ClassifierContainer container : classifiers) {
182 final ClassifierChoice choice = container.getClassifierChoice();
183 final Short index = container.getClassifierId();
185 if (choice instanceof QosClassifierChoice) {
186 addClassifier(index, ((QosClassifierChoice) choice).getClassifier());
188 else if (choice instanceof ExtClassifierChoice) {
189 addExtClassifier(index, ((ExtClassifierChoice) choice).getExtClassifier());
191 else if (choice instanceof Ipv6ClassifierChoice) {
192 addIpv6Classifier(index, ((Ipv6ClassifierChoice) choice).getIpv6Classifier());
195 throw new IllegalStateException("Unknown ClassifierChoice: " + choice);
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;
210 // Protocol -- zero is match any
211 if (qosClassifier.getProtocol() != null) {
212 protocol = Protocol.valueOf(qosClassifier.getProtocol().getValue().shortValue());
214 protocol = Protocol.NONE;
217 // IP Addresss and mask wildcards - addr byte 0 for no match (or match anything)
219 Inet4Address srcAddress = (Inet4Address) getByName("0.0.0.0");
221 if (qosClassifier.getSrcIp() != null) {
222 srcAddress = (Inet4Address) getByName(qosClassifier.getSrcIp().getValue());
225 Inet4Address dstAddress = (Inet4Address) getByName("0.0.0.0");
227 if (qosClassifier.getDstIp() != null) {
228 dstAddress = (Inet4Address) getByName(qosClassifier.getDstIp().getValue());
232 if (qosClassifier.getSrcPort() != null) {
233 srcPort = qosClassifier.getSrcPort().getValue().shortValue();
235 if (qosClassifier.getDstPort() != null) {
236 dstPort = qosClassifier.getDstPort().getValue().shortValue();
238 if (qosClassifier.getTosByte() != null) {
239 tosOverwrite = qosClassifier.getTosByte().getValue().byteValue();
240 if (qosClassifier.getTosMask() != null) {
241 tosMask = qosClassifier.getTosMask().getValue().byteValue();
243 // set default TOS mask
244 tosMask = (byte) 0xff;
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
252 if (qosClassifier.getPriority() != null) {
253 short result = qosClassifier.getPriority();
254 priority = (byte)(result % 255);
257 // push the classifier to the gate request
258 classifiers.add(new org.pcmm.gates.impl.Classifier(protocol, tosOverwrite, tosMask, srcAddress, dstAddress, srcPort,
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());
269 protocol = Protocol.NONE;
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();
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;
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();
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;
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();
317 // set default TOS mask
318 tosMask = (byte) 0xff;
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");
325 if (qosExtClassifier.getSrcIp() != null) {
326 srcIpAddr = getInet4Address(qosExtClassifier.getSrcIp());
329 Inet4Address dstIpAddr = (Inet4Address) getByName("0.0.0.0");
330 if (qosExtClassifier.getDstIp() != null) {
331 dstIpAddr = getInet4Address(qosExtClassifier.getDstIp());
335 Inet4Address srcIpMask = (Inet4Address) getByName("255.255.255.255");
336 if (qosExtClassifier.getSrcIpMask() != null) {
337 srcIpMask = getInet4Address(qosExtClassifier.getSrcIpMask());
340 Inet4Address dstIpMask = (Inet4Address) getByName("255.255.255.255");
341 if (qosExtClassifier.getDstIpMask() != null) {
342 dstIpMask = getInet4Address(qosExtClassifier.getDstIpMask());
345 // TODO - find out what the classifier ID should really be. It was never getting set previously
346 final short classifierId = (short)index;
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;
352 // Convert from Enum to byte for serialization
354 ActivationState activationState = ActivationState.ACTIVE;
355 if (qosExtClassifier.getActivationState() != null) {
356 activationState = ActivationState.valueOf((byte)qosExtClassifier.getActivationState().getIntValue());
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
364 if (qosExtClassifier.getPriority() != null) {
365 short result = qosExtClassifier.getPriority();
366 priority = (byte)(result % 255);
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,
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;
387 private void addIpv6Classifier(final Short index, final Ipv6Classifier qosIpv6Classifier) {
390 if (qosIpv6Classifier.getNextHdr() != null) {
391 nextHdr = qosIpv6Classifier.getNextHdr().getValue().shortValue();
393 // default: match any nextHdr is 256 because nextHdr 0 is Hop-by-Hop option
395 nextHdr = (short) 256;
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");
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]);
410 srcPrefixLen = (byte) 128;
415 // TODO - try to make these two variables immutable
416 Inet6Address dstAddress = (Inet6Address) getByName("0::0");
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;
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();
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;
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();
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;
463 if (qosIpv6Classifier.getTcLow() != null)
464 tcLow = qosIpv6Classifier.getTcLow().getValue().byteValue();
465 else tcLow = (byte) 0x00;
468 if (qosIpv6Classifier.getTcHigh() != null)
469 tcHigh = qosIpv6Classifier.getTcHigh().getValue().byteValue();
470 else tcHigh = (byte) 0x00;
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;
478 FlowLabel flowLabelFlag = FlowLabel.IRRELEVANT;
481 if (qosIpv6Classifier.getFlowLabel() != null) {
482 flowLabelFlag = FlowLabel.VALID;
483 flowLabelId = qosIpv6Classifier.getFlowLabel().intValue();
487 // TODO - find out what the classifier ID should really be. It was never getting set previously
488 final short classifierId = (short)index;
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;
494 // Convert from Enum to byte for serialization
496 ActivationState activationState = ActivationState.ACTIVE;
497 if (qosIpv6Classifier.getActivationState() != null) {
498 activationState = ActivationState.valueOf((byte)qosIpv6Classifier.getActivationState().getIntValue());
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
506 if (qosIpv6Classifier.getPriority() != null) {
507 short result = qosIpv6Classifier.getPriority();
508 priority = (byte)(result % 255);
511 // push the IPv6 classifier to the gate request
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));