/*
* 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.annotations.VisibleForTesting;
import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowTable;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
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.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
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.ofoverlay.rev140528.OfOverlayContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.policy.ExternalImplicitGroup;
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.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* <h1>Manage the table that enforces port security. Initial flows in group-based policy pipeline (table=0)</h1>
*
* Lower-priority flows are leading flows for all traffic incoming from endpoints associated to gbp classifier.<br>
* Created when an {@link Endpoint} is internal and contains {@link OfOverlayContext} augmentation. Several flows of
* this kind are produced.
*<p>
* <i>L2 flow:</i><br>
* Priority = 100<br>
* Matches:<br>
* - in_port, {@link NodeConnectorId}
* - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}<br>
* Actions:<br>
* - {@link GoToTable} SOURCE MAPPER table
*<p>
* <i>L3 flow:</i><br>
* Priority = 120<br>
* Matches:<br>
* - ip, (ethertype)<br>
* - in_port, {@link NodeConnectorId}<br>
* - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}<br>
* - nw_src (source ip address)<br>
* Actions:<br>
* - {@link GoToTable} SOURCE MAPPER table
*<p>
* <i>L3 Arp flow:</i><br>
* Priority = 121<br>
* Matches:<br>
* - arp, (ethertype)<br>
* - in_port, {@link NodeConnectorId}<br>
* - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}<br>
* - arp_spa (arp source transport address)<br>
* Actions:<br>
* - {@link GoToTable} SOURCE MAPPER table
*<p>
* <i>L3 Dhcp dora flow:</i><br>
* Priority = 115<br>
* Matches:<br>
* - ip, (ethertype)<br>
* - in_port, {@link NodeConnectorId}<br>
* - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress}<br>
* - nw_dst (destination ip address)<br>
* Actions:<br>
* - {@link GoToTable} SOURCE MAPPER table
*<p>
* Higher-priority flows providing VLAN support for external networks. Created when node contains external ports
*<p>
* <i>Allow from external:</i><br>
* Priority = 200<br>
* Matches:<br>
* - in_port, {@link NodeConnectorId}<br>
* Actions:<br>
* - {@link GoToTable} INGRESS NAT table
*<p>
* <i>Flow that pops VLAN tag for inbound traffic:</i><br>
* Priority = 210<br>
* See {@link PortSecurityFlows#popVlanTagsOnExternalPortFlows(short, NodeConnectorId, List, int, OfWriter)}
*<p>
* Highest priority flows used to direct traffic coming from tunnel (SFC). These flows are created always
*<p>
* <i>Allow from tunnel:</i><br>
* Priority = 300<br>
* Matches:<br>
* - in_port (has to be tunnel port), {@link NodeConnectorId}<br>
* Actions:<br>
* - {@link GoToTable} SOURCE MAPPER table
*
*/
public class PortSecurity extends FlowTable {
private static final Logger LOG =
LoggerFactory.getLogger(PortSecurity.class);
// Priorities
private static final Integer DROP = 1;
private static final Integer L2FLOW = 100;
private static final Integer DROP_ARP = 110;
private static final Integer DROP_IPV4 = 111;
private static final Integer DROP_IPV6 = 112;
private static final Integer DHCP_DORA = 115;
private static final Integer L3IP_FLOW = 120;
private static final Integer L3ARP_FLOW = 121;
private static final Integer ALLOW_EXTERNAL = 200;
private static final Integer POP_VLAN_TAG_EXTERNAL = 210;
private static final Integer ALLOW_FROM_TUNNEL = 300;
private final short tableId;
public PortSecurity(OfContext ctx, short tableId) {
super(ctx);
this.tableId = tableId;
}
@Override
public short getTableId() {
return tableId;
}
@Override
public void sync(Endpoint endpoint, OfWriter ofWriter) {
NodeId endpointNodeId = ctx.getEndpointManager().getEndpointNodeId(endpoint);
if (endpointNodeId == null) {
LOG.warn("Endpoint {} has no location specified, skipped", endpoint);
return;
}
PortSecurityFlows flows = new PortSecurityFlows(endpointNodeId, tableId);
// Do sync
syncFlows(flows, endpointNodeId, endpoint, ofWriter);
}
@VisibleForTesting
void syncFlows(PortSecurityFlows flows, NodeId nodeId, Endpoint endpoint, OfWriter ofWriter) {
// TODO all "dropFlow" and "allowFlowFromTunnelFlow" flows should be called only once
// Drop all
flows.dropFlow(DROP, null, ofWriter);
// Drop IP traffic that doesn't match a source IP rule
flows.dropFlow(DROP_ARP, FlowUtils.ARP, ofWriter);
flows.dropFlow(DROP_IPV4, FlowUtils.IPv4, ofWriter);
flows.dropFlow(DROP_IPV6, FlowUtils.IPv6, ofWriter);
// Allow traffic from tunnel ports
short sourceMapperId = ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER();
NodeConnectorId vxLanPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
if (vxLanPort != null) {
flows.allowFromTunnelFlow(sourceMapperId, ALLOW_FROM_TUNNEL, vxLanPort, ofWriter);
}
NodeConnectorId vxLanGpePort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlanGpe.class);
if (vxLanGpePort != null) {
flows.allowFromTunnelFlow(sourceMapperId, ALLOW_FROM_TUNNEL, vxLanGpePort, ofWriter);
}
// L3/L2 flows
TenantId tenantId = endpoint.getTenant();
// Internal endpoint
if (EndpointManager.isInternal(endpoint, getExternalImplicitGroupsForTenant(tenantId))) {
NodeConnectorId nodeConnectorId = ctx.getEndpointManager().getEndpointNodeConnectorId(endpoint);
MacAddress macAddress = endpoint.getMacAddress();
if (nodeConnectorId != null && macAddress != null) {
// Allow layer 3 traffic (ARP and IP) with the correct source IP, MAC, and source port
flows.l3Flow(sourceMapperId, endpoint, nodeConnectorId, macAddress, L3IP_FLOW, false, ofWriter);
flows.l3Flow(sourceMapperId, endpoint, nodeConnectorId, macAddress, L3ARP_FLOW, true, ofWriter);
flows.l3DhcpDoraFlow(sourceMapperId, nodeConnectorId, macAddress, DHCP_DORA, ofWriter);
// Allow layer 2 traffic with the correct source MAC and port (lower priority than drop IP rules)
flows.l2flow(sourceMapperId, nodeConnectorId, macAddress, L2FLOW, ofWriter);
}
// External endpoint
} else {
if (LOG.isTraceEnabled()) {
LOG.trace("External Endpoint is ignored in PortSecurity: {}", endpoint);
}
}
// External ports
short ingressNatId = ctx.getPolicyManager().getTABLEID_INGRESS_NAT();
for (NodeConnectorId connectorId : ctx.getSwitchManager().getExternalPorts(nodeId)) {
// TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
if (tenantId != null && ctx.getTenant(tenantId) != null) {
Tenant tenant = ctx.getTenant(tenantId).getTenant();
if (tenant != null && tenant.getForwardingContext() != null &&
tenant.getForwardingContext().getL2FloodDomain() != null) {
List<L2FloodDomain> floodDomains = tenant.getForwardingContext().getL2FloodDomain();
flows.popVlanTagsOnExternalPortFlows(ingressNatId, connectorId, floodDomains,
POP_VLAN_TAG_EXTERNAL, ofWriter);
}
}
// Allowing untagged frames entering policy domain
flows.allowFromExternalPortFlow(ingressNatId, connectorId, ALLOW_EXTERNAL, ofWriter);
}
}
private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
IndexedTenant tenant = ctx.getTenant(tenantId);
if (tenant == null) {
return Collections.emptySet();
}
return tenant.getExternalImplicitGroups();
}
}