/*
* 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.flow;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.Classifier;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.SubjectFeatures;
import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
import org.opendaylight.groupbasedpolicy.resolver.EgKey;
import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
import org.opendaylight.groupbasedpolicy.resolver.Policy;
import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
import org.opendaylight.groupbasedpolicy.resolver.RuleGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.classifier.refs.ClassifierRef;
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.tenants.tenant.EndpointGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup.IntraGroupPolicy;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.contract.subject.Rule;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.subject.feature.instances.ClassifierInstance;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
/**
* Manage the table that enforces policy on the traffic. Traffic is denied
* unless specifically allowed by policy
* @author readams
*/
public class PolicyEnforcer extends FlowTable {
protected static final Logger LOG =
LoggerFactory.getLogger(PolicyEnforcer.class);
public static final short TABLE_ID = 3;
public PolicyEnforcer(OfTable.OfTableCtx ctx) {
super(ctx);
}
@Override
public short getTableId() {
return TABLE_ID;
}
@Override
public void sync(ReadWriteTransaction t, InstanceIdentifier
tiid,
Map flowMap, NodeId nodeId,
PolicyInfo policyInfo, Dirty dirty)
throws Exception {
dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
allowFromTunnel(t, tiid, flowMap, nodeId);
HashSet visitedPairs = new HashSet<>();
for (EgKey sepg : ctx.epManager.getGroupsForNode(nodeId)) {
// Allow traffic within the same endpoint group if the policy
// specifies
IndexedTenant tenant =
ctx.policyResolver.getTenant(sepg.getTenantId());
EndpointGroup group =
tenant.getEndpointGroup(sepg.getEgId());
IntraGroupPolicy igp = group.getIntraGroupPolicy();
int sepgId =
ctx.policyManager.getContextOrdinal(sepg.getTenantId(),
sepg.getEgId());
if (igp == null || igp.equals(IntraGroupPolicy.Allow)) {
allowSameEpg(t, tiid, flowMap, nodeId, sepgId);
}
for (Endpoint src : ctx.epManager.getEPsForNode(nodeId, sepg)) {
if (src.getTenant() == null || src.getEndpointGroup() == null)
continue;
List conds =
ctx.epManager.getCondsForEndpoint(src);
ConditionGroup scg = policyInfo.getEgCondGroup(sepg, conds);
int scgId = ctx.policyManager.getCondGroupOrdinal(scg);
Set peers = policyInfo.getPeers(sepg);
for (EgKey depg : peers) {
int depgId =
ctx.policyManager.getContextOrdinal(depg.getTenantId(),
depg.getEgId());
for (Endpoint dst : ctx.epManager.getEndpointsForGroup(depg)) {
conds = ctx.epManager.getCondsForEndpoint(dst);
ConditionGroup dcg =
policyInfo.getEgCondGroup(new EgKey(dst.getTenant(),
dst.getEndpointGroup()),
conds);
int dcgId = ctx.policyManager.getCondGroupOrdinal(dcg);
CgPair p = new CgPair(depgId, sepgId, dcgId, scgId);
if (visitedPairs.contains(p)) continue;
visitedPairs.add(p);
syncPolicy(t, tiid, flowMap, nodeId, policyInfo,
p, depg, sepg, dcg, scg);
p = new CgPair(sepgId, depgId, scgId, dcgId);
if (visitedPairs.contains(p)) continue;
visitedPairs.add(p);
syncPolicy(t, tiid, flowMap, nodeId, policyInfo,
p, sepg, depg, scg, dcg);
}
}
}
}
}
private void allowSameEpg(ReadWriteTransaction t,
InstanceIdentifier tiid,
Map flowMap, NodeId nodeId,
int sepgId) {
FlowId flowId = new FlowId(new StringBuilder()
.append("intraallow|")
.append(sepgId).toString());
if (visit(flowMap, flowId.getValue())) {
MatchBuilder mb = new MatchBuilder();
addNxRegMatch(mb,
RegMatch.of(NxmNxReg0.class,Long.valueOf(sepgId)),
RegMatch.of(NxmNxReg2.class,Long.valueOf(sepgId)));
FlowBuilder flow = base()
.setId(flowId)
.setMatch(mb.build())
.setPriority(65000)
.setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
writeFlow(t, tiid, flow.build());
}
}
private void allowFromTunnel(ReadWriteTransaction t,
InstanceIdentifier tiid,
Map flowMap, NodeId nodeId) {
NodeConnectorId tunPort =
ctx.switchManager.getTunnelPort(nodeId);
if (tunPort == null) return;
FlowId flowId = new FlowId("tunnelallow");
if (visit(flowMap, flowId.getValue())) {
MatchBuilder mb = new MatchBuilder()
.setInPort(tunPort);
addNxRegMatch(mb,
RegMatch.of(NxmNxReg1.class,Long.valueOf(0xffffff)));
FlowBuilder flow = base()
.setId(flowId)
.setMatch(mb.build())
.setPriority(65000)
.setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
writeFlow(t, tiid, flow.build());
}
}
private void syncPolicy(ReadWriteTransaction t,
InstanceIdentifier tiid,
Map flowMap, NodeId nodeId,
PolicyInfo policyInfo,
CgPair p, EgKey sepg, EgKey depg,
ConditionGroup scg, ConditionGroup dcg)
throws Exception {
// XXX - TODO raise an exception for rules between the same
// endpoint group that are asymmetric
Policy policy = policyInfo.getPolicy(sepg, depg);
List rgs = policy.getRules(scg, dcg);
int priority = 65000;
for (RuleGroup rg : rgs) {
TenantId tenantId = rg.getContractTenant().getId();
IndexedTenant tenant = ctx.policyResolver.getTenant(tenantId);
for (Rule r : rg.getRules()) {
syncDirection(t, tiid, flowMap, nodeId, tenant,
p, r, Direction.In, priority);
syncDirection(t, tiid, flowMap, nodeId, tenant,
p, r, Direction.Out, priority);
priority -= 1;
}
}
}
private void syncDirection(ReadWriteTransaction t,
InstanceIdentifier tiid,
Map flowMap, NodeId nodeId,
IndexedTenant contractTenant,
CgPair p, Rule r, Direction d, int priority) {
for (ClassifierRef cr : r.getClassifierRef()) {
if (cr.getDirection() != null &&
!cr.getDirection().equals(Direction.Bidirectional) &&
!cr.getDirection().equals(d))
continue;
StringBuilder idb = new StringBuilder();
// XXX - TODO - implement connection tracking (requires openflow
// extension and data plane support)
MatchBuilder baseMatch = new MatchBuilder();
if (d.equals(Direction.In)) {
idb.append(p.sepg)
.append("|")
.append(p.scgId)
.append("|")
.append(p.depg)
.append("|")
.append(p.dcgId)
.append("|")
.append(priority);
addNxRegMatch(baseMatch,
RegMatch.of(NxmNxReg0.class,Long.valueOf(p.sepg)),
RegMatch.of(NxmNxReg1.class,Long.valueOf(p.scgId)),
RegMatch.of(NxmNxReg2.class,Long.valueOf(p.depg)),
RegMatch.of(NxmNxReg3.class,Long.valueOf(p.dcgId)));
} else {
idb.append(p.depg)
.append("|")
.append(p.dcgId)
.append("|")
.append(p.sepg)
.append("|")
.append(p.scgId)
.append("|")
.append(priority);
addNxRegMatch(baseMatch,
RegMatch.of(NxmNxReg0.class,Long.valueOf(p.depg)),
RegMatch.of(NxmNxReg1.class,Long.valueOf(p.dcgId)),
RegMatch.of(NxmNxReg2.class,Long.valueOf(p.sepg)),
RegMatch.of(NxmNxReg3.class,Long.valueOf(p.scgId)));
}
ClassifierInstance ci = contractTenant.getClassifier(cr.getName());
if (ci == null) {
// XXX TODO fail the match and raise an exception
LOG.warn("Classifier instance {} not found",
cr.getName().getValue());
return;
}
Classifier cfier = SubjectFeatures
.getClassifier(ci.getClassifierDefinitionId());
if (cfier == null) {
// XXX TODO fail the match and raise an exception
LOG.warn("Classifier definition {} not found",
ci.getClassifierDefinitionId().getValue());
return;
}
List matches = Collections.singletonList(baseMatch);
Map params = new HashMap<>();
for (ParameterValue v : ci.getParameterValue()) {
if (v.getName() == null) continue;
if (v.getIntValue() != null) {
params.put(v.getName().getValue(), v.getIntValue());
} else if (v.getStringValue() != null) {
params.put(v.getName().getValue(), v.getStringValue());
}
}
matches = cfier.updateMatch(matches, params);
String baseId = idb.toString();
FlowBuilder flow = base()
.setPriority(Integer.valueOf(priority));
for (MatchBuilder match : matches) {
Match m = match.build();
FlowId flowId = new FlowId(baseId + "|" + m.toString());
if (visit(flowMap, flowId.getValue())) {
flow.setMatch(m)
.setId(flowId)
.setPriority(Integer.valueOf(priority))
.setInstructions(instructions(applyActionIns(nxOutputRegAction(NxmNxReg7.class))));
writeFlow(t, tiid, flow.build());
}
}
}
}
@Immutable
private static class CgPair {
private final int sepg;
private final int depg;
private final int scgId;
private final int dcgId;
public CgPair(int sepg, int depg, int scgId, int dcgId) {
super();
this.sepg = sepg;
this.depg = depg;
this.scgId = scgId;
this.dcgId = dcgId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + dcgId;
result = prime * result + depg;
result = prime * result + scgId;
result = prime * result + sepg;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CgPair other = (CgPair) obj;
if (dcgId != other.dcgId)
return false;
if (depg != other.depg)
return false;
if (scgId != other.scgId)
return false;
if (sepg != other.sepg)
return false;
return true;
}
}
}