/*
* 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.mapper.portsecurity;
import com.google.common.base.Preconditions;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowIdUtils;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
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.Flow;
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.InstructionsBuilder;
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.flow.types.rev131026.instruction.list.Instruction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
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.model.match.types.rev131026.match.Layer3Match;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import java.util.ArrayList;
import java.util.List;
/**
* Building and writing of specific flows according to data from {@link PortSecurity}
*/
class PortSecurityFlows {
private final NodeId nodeId;
private final Short tableId;
PortSecurityFlows(NodeId nodeId, Short tableId) {
this.nodeId = Preconditions.checkNotNull(nodeId);
this.tableId = Preconditions.checkNotNull(tableId);
}
/**
* Default flow which drops all incoming traffic
*
* @param priority of flow in the table
* @param etherType can be set as specific protocol to match
* @param ofWriter flow writer
*/
void dropFlow(int priority, Long etherType, OfWriter ofWriter) {
FlowId flowId;
FlowBuilder flowBuilder = FlowUtils.base(tableId)
.setPriority(priority)
.setInstructions(FlowUtils.dropInstructions());
if (etherType != null) {
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(FlowUtils.ethernetMatch(null, null, etherType));
Match match = matchBuilder.build();
flowId = FlowIdUtils.newFlowId(tableId, "drop", match);
flowBuilder.setMatch(match);
} else {
flowId = FlowIdUtils.newFlowId("dropAll");
}
flowBuilder.setId(flowId);
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* All traffic coming from tunnel match (except SFC in some cases)
*
* @param goToTable tableId for goToTable instruction
* @param priority of flow in the table
* @param tunnelTypePort tunnel node connector ID
* @param ofWriter flow writer
*/
void allowFromTunnelFlow(short goToTable, int priority, NodeConnectorId tunnelTypePort, OfWriter ofWriter) {
Preconditions.checkNotNull(tunnelTypePort);
Match match = new MatchBuilder()
.setInPort(tunnelTypePort)
.build();
FlowId flowId = FlowIdUtils.newFlowId(tableId, "allow", match);
FlowBuilder flowBuilder = FlowUtils.base(tableId)
.setId(flowId)
.setPriority(priority)
.setMatch(match)
.setInstructions(FlowUtils.gotoTableInstructions(goToTable));
flowBuilder.build();
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Allows IP or ARP L3 traffic. Match consists from source IP & MAC address plus port. All traffic is redirected to
* source mapper
*
* @param goToTable tableId for goToTable instruction
* @param endpoint with {@link IpAddress}, {@link MacAddress} and {@link NodeConnectorId}
* @param connectorId which represents {@link Endpoint} on {@link Node}
* @param macAddress of endpoint
* @param priority of flow in the table
* @param arp whether create ip or arp flow
* @param ofWriter flow writer
*/
void l3Flow(short goToTable, Endpoint endpoint, NodeConnectorId connectorId, MacAddress macAddress,
int priority, boolean arp, OfWriter ofWriter) {
if (endpoint.getL3Address() != null) {
for (L3Address l3Address : endpoint.getL3Address()) {
if (l3Address.getIpAddress() == null) {
continue;
}
Match match;
if (arp) {
match = createL3ArpMatch(connectorId, macAddress, l3Address.getIpAddress());
} else {
match = createL3IpMatch(connectorId, macAddress, l3Address.getIpAddress());
}
if (match == null) {
continue;
}
FlowId flowid = FlowIdUtils.newFlowId(tableId, "L3", match);
Flow flow = FlowUtils.base(tableId)
.setPriority(priority)
.setId(flowid)
.setMatch(match)
.setInstructions(FlowUtils.gotoTableInstructions(goToTable))
.build();
ofWriter.writeFlow(nodeId, tableId, flow);
}
}
}
/**
* DHCP flow with broadcast destination address
*
* @param goToTable tableId for goToTable instruction
* @param connectorId which represents {@link Endpoint} on {@link Node}
* @param macAddress of endpoint
* @param priority of flow in the table
* @param ofWriter flow writer
*/
void l3DhcpDoraFlow(short goToTable, NodeConnectorId connectorId, MacAddress macAddress, int priority,
OfWriter ofWriter) {
//TODO: Handle IPv6 DORA
Long etherType = FlowUtils.IPv4;
// DHCP DORA destination is broadcast
String iKey = "255.255.255.255/32";
Layer3Match layer3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(iKey)).build();
Match match = new MatchBuilder()
.setEthernetMatch(FlowUtils.ethernetMatch(macAddress, null, etherType))
.setLayer3Match(layer3Match)
.setInPort(connectorId)
.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "dhcp", match);
Flow flow = FlowUtils.base(tableId)
.setPriority(priority)
.setId(flowid)
.setMatch(match)
.setInstructions(FlowUtils.gotoTableInstructions(goToTable))
.build();
ofWriter.writeFlow(nodeId, tableId, flow);
}
/**
* Allow L2 traffic
*
* @param goToTable tableId for goToTable instruction
* @param connectorId which represents {@link Endpoint} on {@link Node}
* @param macAddress of endpoint
* @param priority of flow in the table
* @param ofWriter flow writer
*/
void l2flow(short goToTable, NodeConnectorId connectorId, MacAddress macAddress, int priority,
OfWriter ofWriter) {
Match match = new MatchBuilder()
.setEthernetMatch(FlowUtils.ethernetMatch(macAddress, null, null))
.setInPort(connectorId)
.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "L2", match);
FlowBuilder flowBuilder = FlowUtils.base(tableId)
.setPriority(priority)
.setId(flowid)
.setMatch(match)
.setInstructions(FlowUtils.gotoTableInstructions(goToTable));
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Pops VLAN tag for inbound traffic. Packets are sent to ingress NAT table
*
* @param goToTable tableId for goToTable instruction
* @param connectorId should be external for now
* @param l2FloodDomains list of {@link L2FloodDomain} which could contain {@link Segmentation} augmentation
* @param priority of flow in the table
* @param ofWriter flow writer
*/
void popVlanTagsOnExternalPortFlows(short goToTable, NodeConnectorId connectorId, List<L2FloodDomain> l2FloodDomains,
int priority, OfWriter ofWriter) {
for (L2FloodDomain l2Fd : l2FloodDomains) {
Segmentation segmentation = l2Fd.getAugmentation(Segmentation.class);
if (segmentation != null) {
Integer vlanId = segmentation.getSegmentationId();
Match match = new MatchBuilder()
.setVlanMatch(FlowUtils.vlanMatch(vlanId, true))
.setInPort(connectorId)
.build();
List<Instruction> instructions = new ArrayList<>();
instructions.add(FlowUtils.popVlanInstruction(0));
instructions.add(new InstructionBuilder().setOrder(1)
// TODO for now matches on external flows are passed to ingress nat table
.setInstruction(FlowUtils.gotoTableIns(goToTable))
.build());
FlowId flowid = FlowIdUtils.newFlowId(tableId, "allowExternalPopVlan", match);
Flow flow = FlowUtils.base(tableId).setPriority(priority)
.setId(flowid)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(instructions).build())
.build();
ofWriter.writeFlow(nodeId, tableId, flow);
}
}
}
/**
* Allow untagged frames enter policy domain. Packets are sent to ingress NAT table
*
* @param goToTable tableId for goToTable instruction
* @param connectorId should be external for now
* @param priority of flow in the table
* @param ofWriter flow writer
*/
void allowFromExternalPortFlow(short goToTable, NodeConnectorId connectorId, int priority, OfWriter ofWriter) {
Match match = new MatchBuilder().setInPort(connectorId).build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "allowExternal", match);
FlowBuilder flowBuilder = FlowUtils.base(tableId).setId(flowid)
.setPriority(priority)
.setMatch(match)
.setInstructions(FlowUtils.gotoTableInstructions(goToTable));
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
private Match createL3IpMatch(NodeConnectorId connectorId, MacAddress macAddress, IpAddress l3IpAddress) {
String iKey;
Long etherType;
Layer3Match layer3Match;
if (l3IpAddress.getIpv4Address() != null) {
iKey = l3IpAddress.getIpv4Address().getValue() + "/32";
etherType = FlowUtils.IPv4;
layer3Match = new Ipv4MatchBuilder().setIpv4Source(new Ipv4Prefix(iKey)).build();
} else if (l3IpAddress.getIpv6Address() != null) {
iKey = l3IpAddress.getIpv6Address().getValue() + "/128";
etherType = FlowUtils.IPv6;
layer3Match = new Ipv6MatchBuilder().setIpv6Source(new Ipv6Prefix(iKey)).build();
} else {
return null;
}
return new MatchBuilder()
.setEthernetMatch(FlowUtils.ethernetMatch(macAddress, null, etherType))
.setLayer3Match(layer3Match)
.setInPort(connectorId)
.build();
}
private Match createL3ArpMatch(NodeConnectorId connectorId, MacAddress macAddress, IpAddress l3IpAddress) {
String iKey;
Long etherType;
Layer3Match layer3Match;
if (l3IpAddress.getIpv4Address() != null) {
iKey = l3IpAddress.getIpv4Address().getValue() + "/32";
etherType = FlowUtils.ARP;
layer3Match = new ArpMatchBuilder().setArpSourceTransportAddress(new Ipv4Prefix(iKey)).build();
} else {
// Ipv6 has no ip case
return null;
}
return new MatchBuilder()
.setEthernetMatch(FlowUtils.ethernetMatch(macAddress, null, etherType))
.setLayer3Match(layer3Match)
.setInPort(connectorId)
.build();
}
}