/*
* Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import org.opendaylight.groupbasedpolicy.api.sf.IpProtoClassifierDefinition;
import org.opendaylight.groupbasedpolicy.api.sf.L4ClassifierDefinition;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.ParameterValue;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.instance.parameter.value.RangeValue;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.parameters.type.parameter.type.IntBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.has.parameters.type.parameter.type.RangeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValues;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.supported.classifier.definition.SupportedParameterValuesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValueInRange;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported._int.value.fields.SupportedIntValueInRangeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported.range.value.fields.SupportedRangeValue;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.supported.range.value.fields.SupportedRangeValueBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer4Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.SctpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.SctpMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatchBuilder;
/**
* Match against TCP or UDP, and source and/or destination ports
*/
public class L4Classifier extends Classifier {
protected L4Classifier(Classifier parent) {
super(parent);
}
@Override
public ClassifierDefinitionId getId() {
return L4ClassifierDefinition.ID;
}
@Override
public ClassifierDefinition getClassifierDefinition() {
return L4ClassifierDefinition.DEFINITION;
}
@Override
public List<SupportedParameterValues> getSupportedParameterValues() {
List<SupportedIntValueInRange> allPossiblePortsIntInRange =
ImmutableList.of(new SupportedIntValueInRangeBuilder().setMin(1L).setMax(65535L).build());
List<SupportedRangeValue> allPossiblePortsRange =
ImmutableList.of(new SupportedRangeValueBuilder().setMin(1L).setMax(65535L).build());
SupportedParameterValues srcPorts = new SupportedParameterValuesBuilder()
.setParameterName(new ParameterName(L4ClassifierDefinition.SRC_PORT_PARAM))
.setParameterType(new IntBuilder().setSupportedIntValueInRange(allPossiblePortsIntInRange).build())
.build();
SupportedParameterValues dstPorts = new SupportedParameterValuesBuilder()
.setParameterName(new ParameterName(L4ClassifierDefinition.DST_PORT_PARAM))
.setParameterType(new IntBuilder().setSupportedIntValueInRange(allPossiblePortsIntInRange).build())
.build();
SupportedParameterValues srcPortsRange = new SupportedParameterValuesBuilder()
.setParameterName(new ParameterName(L4ClassifierDefinition.SRC_PORT_RANGE_PARAM))
.setParameterType(new RangeBuilder().setSupportedRangeValue(allPossiblePortsRange).build())
.build();
SupportedParameterValues dstPortsRange = new SupportedParameterValuesBuilder()
.setParameterName(new ParameterName(L4ClassifierDefinition.DST_PORT_RANGE_PARAM))
.setParameterType(new RangeBuilder().setSupportedRangeValue(allPossiblePortsRange).build())
.build();
return ImmutableList.of(srcPorts, dstPorts, srcPortsRange, dstPortsRange);
}
@Override
protected void checkPresenceOfRequiredParams(Map<String, ParameterValue> params) {
validatePortParam(params, L4ClassifierDefinition.SRC_PORT_PARAM, L4ClassifierDefinition.SRC_PORT_RANGE_PARAM);
validatePortParam(params, L4ClassifierDefinition.DST_PORT_PARAM, L4ClassifierDefinition.DST_PORT_RANGE_PARAM);
validateRange(params, L4ClassifierDefinition.SRC_PORT_RANGE_PARAM);
validateRange(params, L4ClassifierDefinition.DST_PORT_RANGE_PARAM);
}
private void validatePortParam(Map<String, ParameterValue> params, String portParam, String portRangeParam) {
if (params.get(portParam) != null) {
StringBuilder paramLog = new StringBuilder();
if (params.get(portParam).getIntValue() == null) {
paramLog.append("Value of ").append(portParam).append(" parameter " + MSG_NOT_SPECIFIED);
throw new IllegalArgumentException(paramLog.toString());
}
if (params.get(portRangeParam) != null) {
paramLog.append("Source port parameters ")
.append(portParam)
.append(" and ")
.append(portRangeParam)
.append(" are " + MSG_MUTUALLY_EXCLUSIVE);
throw new IllegalArgumentException(paramLog.toString());
}
}
}
private void validateRange(Map<String, ParameterValue> params, String portRangeParam) {
if (params.get(portRangeParam) != null) {
validateRangeValue(params.get(portRangeParam).getRangeValue());
}
}
private void validateRangeValue(RangeValue rangeValueParam) {
if (rangeValueParam == null) {
throw new IllegalArgumentException(
"Range parameter is specified but value is " + Classifier.MSG_NOT_PRESENT);
}
final Long min = rangeValueParam.getMin();
final Long max = rangeValueParam.getMax();
if (min > max) {
throw new IllegalArgumentException(
MSG_RANGE_VALUE_MISMATCH + " MIN " + min + " is greater than MAX " + max + ".");
}
}
@Override
public List<MatchBuilder> update(List<MatchBuilder> matches, Map<String, ParameterValue> params) {
Set<Long> sPorts = new HashSet<>();
Set<Long> dPorts = new HashSet<>();
addToPortSet(params, L4ClassifierDefinition.SRC_PORT_PARAM, L4ClassifierDefinition.SRC_PORT_RANGE_PARAM,
sPorts);
addToPortSet(params, L4ClassifierDefinition.DST_PORT_PARAM, L4ClassifierDefinition.DST_PORT_RANGE_PARAM,
dPorts);
List<MatchBuilder> newMatches = new ArrayList<>();
for (MatchBuilder matchBuilder : matches) {
Layer4Match l4Match = matchBuilder.getLayer4Match();
Set<? extends Layer4Match> l4Matches = null;
if (l4Match == null) {
l4Match = resolveL4Match(params);
}
l4Matches = createL4Matches(l4Match, sPorts, dPorts);
for (Layer4Match newL4Match : l4Matches) {
newMatches.add(new MatchBuilder(matchBuilder.build()).setLayer4Match(newL4Match));
}
}
return newMatches;
}
private void addToPortSet(Map<String, ParameterValue> params, String portParam, String portRangeParam,
Set<Long> portSet) {
if (params.get(portParam) != null) {
portSet.add(params.get(portParam).getIntValue());
} else if (params.get(portRangeParam) != null) {
portSet.addAll(createSetFromRange(params.get(portRangeParam).getRangeValue()));
}
}
private Layer4Match resolveL4Match(Map<String, ParameterValue> params) {
Long ipProto = IpProtoClassifier.getIpProtoValue(params);
if (ipProto == null) {
throw new IllegalArgumentException("Parameter " + IpProtoClassifierDefinition.PROTO_PARAM + " is missing.");
}
if (IpProtoClassifierDefinition.UDP_VALUE.equals(ipProto)) {
return new UdpMatchBuilder().build();
} else if (IpProtoClassifierDefinition.TCP_VALUE.equals(ipProto)) {
return new TcpMatchBuilder().build();
} else if (IpProtoClassifierDefinition.SCTP_VALUE.equals(ipProto)) {
return new SctpMatchBuilder().build();
}
throw new IllegalArgumentException("Parameter " + IpProtoClassifierDefinition.PROTO_PARAM + ": value " + ipProto
+ " is " + Classifier.MSG_NOT_SUPPORTED);
}
private Set<Long> createSetFromRange(RangeValue rangeValueParam) {
Set<Long> res = new HashSet<>();
if (rangeValueParam != null) {
final Long min = rangeValueParam.getMin();
final Long max = rangeValueParam.getMax();
for (long val = min; val <= max; val++) {
res.add(val);
}
}
return res;
}
private Set<? extends Layer4Match> createL4Matches(Layer4Match l4Match, Set<Long> sPorts, Set<Long> dPorts) {
Set<? extends Layer4Match> l4Matches = null;
if (l4Match instanceof UdpMatch) {
l4Matches = createUdpMatches((UdpMatch) l4Match, sPorts, dPorts);
} else if (l4Match instanceof TcpMatch) {
l4Matches = createTcpMatches((TcpMatch) l4Match, sPorts, dPorts);
} else if (l4Match instanceof SctpMatch) {
l4Matches = createSctpMatches((SctpMatch) l4Match, sPorts, dPorts);
}
return l4Matches;
}
private Set<UdpMatch> createUdpMatches(UdpMatch udpMatch, Set<Long> sPorts, Set<Long> dPorts) {
Set<UdpMatch> udpMatches = new HashSet<>();
if (!sPorts.isEmpty() && dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
equalOrNotSetValidation(udpMatch.getUdpSourcePort(), srcPort.longValue());
udpMatches
.add(new UdpMatchBuilder(udpMatch).setUdpSourcePort(new PortNumber(srcPort.intValue())).build());
}
} else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(udpMatch.getUdpDestinationPort(), dstPort.longValue());
udpMatches.add(new UdpMatchBuilder(udpMatch).setUdpDestinationPort(new PortNumber(dstPort.intValue()))
.build());
}
} else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(udpMatch.getUdpSourcePort(), srcPort.longValue());
equalOrNotSetValidation(udpMatch.getUdpDestinationPort(), dstPort.longValue());
udpMatches.add(new UdpMatchBuilder(udpMatch).setUdpSourcePort(new PortNumber(srcPort.intValue()))
.setUdpDestinationPort(new PortNumber(dstPort.intValue()))
.build());
}
}
}
return udpMatches;
}
private Set<TcpMatch> createTcpMatches(TcpMatch tcpMatch, Set<Long> sPorts, Set<Long> dPorts) {
Set<TcpMatch> tcpMatches = new HashSet<>();
if (!sPorts.isEmpty() && dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
equalOrNotSetValidation(tcpMatch.getTcpSourcePort(), srcPort.longValue());
tcpMatches
.add(new TcpMatchBuilder(tcpMatch).setTcpSourcePort(new PortNumber(srcPort.intValue())).build());
}
} else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(tcpMatch.getTcpDestinationPort(), dstPort.longValue());
tcpMatches.add(new TcpMatchBuilder(tcpMatch).setTcpDestinationPort(new PortNumber(dstPort.intValue()))
.build());
}
} else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(tcpMatch.getTcpSourcePort(), srcPort.longValue());
equalOrNotSetValidation(tcpMatch.getTcpDestinationPort(), dstPort.longValue());
tcpMatches.add(new TcpMatchBuilder(tcpMatch).setTcpSourcePort(new PortNumber(srcPort.intValue()))
.setTcpDestinationPort(new PortNumber(dstPort.intValue()))
.build());
}
}
}
return tcpMatches;
}
private Set<SctpMatch> createSctpMatches(SctpMatch sctpMatch, Set<Long> sPorts, Set<Long> dPorts) {
Set<SctpMatch> sctpMatches = new HashSet<>();
if (!sPorts.isEmpty() && dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
equalOrNotSetValidation(sctpMatch.getSctpSourcePort(), srcPort.longValue());
sctpMatches
.add(new SctpMatchBuilder(sctpMatch).setSctpSourcePort(new PortNumber(srcPort.intValue())).build());
}
} else if (sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(sctpMatch.getSctpDestinationPort(), dstPort.longValue());
sctpMatches.add(new SctpMatchBuilder(sctpMatch)
.setSctpDestinationPort(new PortNumber(dstPort.intValue())).build());
}
} else if (!sPorts.isEmpty() && !dPorts.isEmpty()) {
for (Long srcPort : sPorts) {
for (Long dstPort : dPorts) {
equalOrNotSetValidation(sctpMatch.getSctpSourcePort(), srcPort.longValue());
equalOrNotSetValidation(sctpMatch.getSctpDestinationPort(), dstPort.longValue());
sctpMatches
.add(new SctpMatchBuilder(sctpMatch).setSctpSourcePort(new PortNumber(srcPort.intValue()))
.setSctpDestinationPort(new PortNumber(dstPort.intValue()))
.build());
}
}
}
return sctpMatches;
}
private void equalOrNotSetValidation(PortNumber portInMatch, long paramValue) {
if (portInMatch != null) {
if (paramValue != portInMatch.getValue().longValue()) {
throw new IllegalArgumentException(Classifier.MSG_CLASSIFICATION_CONFLICT_DETECTED + " for port values "
+ portInMatch.getValue().longValue() + " and " + paramValue + ". It is not allowed "
+ "to assign different values to the same parameter among all the classifiers within one rule.");
}
}
}
@Override
public void checkPrereqs(List<MatchBuilder> matches) {
for (MatchBuilder match : matches) {
Long proto = null;
try {
proto = Long.valueOf(match.getIpMatch().getIpProtocol().longValue());
} catch (NullPointerException e) {
throw new IllegalArgumentException(
"Parameter " + IpProtoClassifierDefinition.PROTO_PARAM + " " + MSG_IS_MISSING);
}
if (!IpProtoClassifierDefinition.TCP_VALUE.equals(proto)
&& !IpProtoClassifierDefinition.UDP_VALUE.equals(proto)
&& !IpProtoClassifierDefinition.SCTP_VALUE.equals(proto)) {
throw new IllegalArgumentException("Value of parameter " + IpProtoClassifierDefinition.PROTO_PARAM
+ " is " + Classifier.MSG_NOT_SUPPORTED);
}
}
}
}