/* * 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 static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxNsiMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxNspMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxTunIdMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxTunIpv4DstMatch; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc1RegAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadNshc2RegAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadRegAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunGpeNpAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIPv4Action; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxPopNshAction; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.mapper.policyenforcer.NetworkElements; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.node.SwitchManager; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sf.ChainAction; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.sfcutils.SfcNshHeader; 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.Ipv4Address; 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.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.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.instruction.go.to.table._case.GoToTable; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction; 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.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.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.NxmNxReg4; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; /** * <h1>Creation of flows related to service chain</h1> * * These flows are built across most of gbp of tables and have higher priority than basic flows. It ensures, that * packet redirected to chain will be sent to tunnel output * <p> * * Flow that allows ALL traffic incoming from chain last hop * <p> * <i>Allow from chain flow</i><br> * Table = 0<br> * Priority = 1200<br> * Matches:<br> * - Nshc1<br> * - Nsp<br> * - Nsi<br> * - in_port (tunnel port) {@link NodeConnectorId}<br> * Actions:<br> * - {@link GoToTable} SOURCE MAPPER table * <p> * TODO: looks like duplicity, the same flow is created in policy enforcer * <i>Allow from chain tunnel</i> * Table = 4<br> * Priority = 65000<br> * Matches:<br> * - in_port (tunnel port) {@link NodeConnectorId}<br> * - Reg7 (fixed value 0xffffff) {@link NxmNxReg7} * Actions:<br> * - {@link GoToTable} SOURCE MAPPER table * <p> * <i>Create external flow</i> * Table = 6<br> * Priority = 1000 (if dst node == src node, priority = 1500)<br> * Matches:<br> * - Reg6 {@link NxmNxReg6}<br> * - tunnel ID<br> * - nsp<br> * - nsi<br> * - tun_dst (only if dst node == src node)<br> * Actions:<br> * - set nshc1<br> * - set nshc2<br> * - load tunnel ID<br> * - load tunnel ipv4<br> * - output:(tunnel port)<br> * <p> * <i>Chain tunnel flow</i><br> * Table = 2<br> * Priority = 150<br> * Matches:<br> * - in_port (tunnel port) {@link NodeConnectorId}<br> * - tunnel ID<br> * - nsp<br> * - nsi<br> * Actions:<br> * - Reg0 {@link NxmNxReg0}<br> * - Reg1 {@link NxmNxReg1}<br> * - Reg4 {@link NxmNxReg4}<br> * - Reg5 {@link NxmNxReg5}<br> * - Reg6 {@link NxmNxReg6}<br> * - {@link GoToTable} DESTINATION MAPPER table<br> * <p> * <i>Chain broadcast flow</i><br> * Table = 2<br> * Priority = 150<br> * Matches:<br> * - in_port (tunnel port) {@link NodeConnectorId}<br> * - tunnel ID<br> * - nsp<br> * - nsi<br> * Actions:<br> * - load Reg5 {@link NxmNxReg5}<br> * - {@link GoToTable} DESTINATION MAPPER table<br> */ public class ChainActionFlows { private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class); private static final short TUN_GPE_NP_NSH = 0x4; public ChainActionFlows() { } public static void createChainTunnelFlows(SfcNshHeader sfcNshHeader, NetworkElements netElements, OfWriter ofWriter, OfContext ctx, Direction direction) { NodeId localNodeId = netElements.getLocalNodeId(); EndpointFwdCtxOrdinals epOrdinals = netElements.getSrcEpOrdinals(); NodeConnectorId localNodeTunPort = ctx.getSwitchManager().getTunnelPort(localNodeId, TunnelTypeVxlanGpe.class); Ipv4Address tunDestAddress = ctx.getSwitchManager() .getTunnelIP(netElements.getDstNodeId(), TunnelTypeVxlanGpe.class) .getIpv4Address(); if (localNodeTunPort == null) { LOG.error("createChainTunnelFlows: No valid VXLAN GPE tunnel for Node {} ", localNodeId); return; } if (direction.equals(Direction.In)) { ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_PORTSECURITY(), allowFromChainPort( sfcNshHeader, localNodeTunPort, ctx.getPolicyManager().getTABLEID_PORTSECURITY(), ctx)); for (Flow flow : createChainTunnelFlow(sfcNshHeader, localNodeTunPort, netElements, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), ctx)) { ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), flow); } ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER(), allowFromChainTunnel(localNodeTunPort, ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER())); ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), createChainBroadcastFlow(sfcNshHeader, localNodeTunPort, epOrdinals, ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER(), ctx)); } else { ofWriter.writeFlow(localNodeId, ctx.getPolicyManager().getTABLEID_EXTERNAL_MAPPER(), createExternalFlow(sfcNshHeader, localNodeTunPort, netElements, ctx.getPolicyManager(), ctx.getSwitchManager(), tunDestAddress)); } } private static Flow createChainBroadcastFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort, EndpointFwdCtxOrdinals epFwdCtxOrds, short tableId, OfContext ctx) { int fdId = epFwdCtxOrds.getFdId(); MatchBuilder mb = new MatchBuilder().setInPort(tunPort); addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain()); addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain()); addNxTunIdMatch(mb, fdId); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId)); Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainbroadcast", match); FlowBuilder flowb = base(tableId).setId(flowId) .setPriority(150) .setMatch(match) .setInstructions(instructions(applyActionIns(fdReg), gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER()))); return flowb.build(); } private static Flow createExternalFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort, NetworkElements netElements, PolicyManager policyManager, SwitchManager switchManager, Ipv4Address ipTunDest) { short tableId = policyManager.getTABLEID_EXTERNAL_MAPPER(); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadC1; org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action outputAction; Integer priority = 1000; int matchTunnelId = netElements.getSrcEpOrdinals().getTunnelId(); long setTunnelId = netElements.getDstEpOrdinals().getTunnelId(); Long l3c = (long) netElements.getSrcEpOrdinals().getL3Id(); loadC1 = nxLoadNshc1RegAction(sfcNshHeader.getNshMetaC1()); // Test for if SFF is on same node IpAddress ipAddress = switchManager.getTunnelIP(netElements.getLocalNodeId(), TunnelTypeVxlanGpe.class); if (ipAddress != null && ipAddress.getIpv4Address().equals(sfcNshHeader.getNshTunIpDst())) { Integer newPort = returnOfPortFromNodeConnector(tunPort); outputAction = FlowUtils.createActionResubmit(newPort, policyManager.getTABLEID_SFC_EGRESS()); } else { outputAction = outputAction(tunPort); } org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadC2 = nxLoadNshc2RegAction(setTunnelId); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunVnid = nxLoadTunIdAction(BigInteger.valueOf(setTunnelId), false); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunDest = nxLoadTunIPv4Action(sfcNshHeader.getNshTunIpDst().getValue(), false); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadTunGpeNp = nxLoadTunGpeNpAction(BigInteger.valueOf(TUN_GPE_NP_NSH), false); MatchBuilder mb = new MatchBuilder(); addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, l3c)); addNxTunIdMatch(mb, matchTunnelId); addNxNspMatch(mb, sfcNshHeader.getNshNspToChain()); addNxNsiMatch(mb, sfcNshHeader.getNshNsiToChain()); if (!netElements.getDstNodeId().equals(netElements.getSrcNodeId())) { addNxTunIpv4DstMatch(mb, ipTunDest); priority = 1500; } Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainexternal", match); FlowBuilder flowb = base(tableId).setId(flowId).setPriority(priority).setMatch(match).setInstructions( instructions(applyActionIns(loadC1, loadC2, loadChainTunDest, loadChainTunVnid, loadTunGpeNp, outputAction))); return flowb.build(); } private static List<Flow> createChainTunnelFlow(SfcNshHeader sfcNshHeader, NodeConnectorId tunPort, NetworkElements netElements, short tableId, OfContext ctx) { int egId = netElements.getDstEpOrdinals().getEpgId(); int bdId = netElements.getDstEpOrdinals().getBdId(); int fdId = netElements.getDstEpOrdinals().getFdId(); int l3Id = netElements.getDstEpOrdinals().getL3Id(); int tunnelId = netElements.getSrcEpOrdinals().getTunnelId(); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId)); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(0xffffff)); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId)); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId)); org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id)); List<Flow> flows = new ArrayList<>(); for (L3Address address : netElements.getDstEp().getL3Address()) { Layer3Match l3Match = null; MatchBuilder mb = null; if (address.getIpAddress() != null) { if (address.getIpAddress().getIpv4Address() != null) { l3Match = new Ipv4MatchBuilder() .setIpv4Source(new Ipv4Prefix(address.getIpAddress().getIpv4Address().getValue() + "/32")) .build(); mb = new MatchBuilder().setInPort(tunPort) .setLayer3Match(l3Match) .setEthernetMatch(FlowUtils.ethernetMatch(null, null, FlowUtils.IPv4)); } else if (address.getIpAddress().getIpv6Address() != null) { l3Match = new Ipv6MatchBuilder() .setIpv6Source(new Ipv6Prefix(address.getIpAddress().getIpv6Address().getValue() + "/128")) .build(); mb = new MatchBuilder().setInPort(tunPort) .setLayer3Match(l3Match) .setEthernetMatch(FlowUtils.ethernetMatch(null, null, FlowUtils.IPv6)); } } addNxTunIdMatch(mb, tunnelId); addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain()); addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain()); Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(tableId, "chaintunnel", match); FlowBuilder flowb = base(tableId).setId(flowId).setPriority(150).setMatch(match).setInstructions( instructions(applyActionIns(segReg, scgReg, bdReg, fdReg, vrfReg), gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER()))); flows.add(flowb.build()); } return flows; } private static Flow allowFromChainPort(SfcNshHeader sfcNshHeader, NodeConnectorId port, short tableId, OfContext ctx) { // Matching on last NSP/NSI that SFF leaves on but with C1=0 MatchBuilder mb = new MatchBuilder(); FlowUtils.addNxNshc1RegMatch(mb, 0L); FlowUtils.addNxNsiMatch(mb, sfcNshHeader.getNshNsiFromChain()); FlowUtils.addNxNspMatch(mb, sfcNshHeader.getNshNspFromChain()); Match match = mb.setInPort(port).build(); FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainport", match); FlowBuilder flowb = base(tableId).setId(flowId).setPriority(1200).setMatch(match).setInstructions( FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER())); return flowb.build(); } private static Flow allowFromChainTunnel(NodeConnectorId tunPort, short tableId) { MatchBuilder mb = new MatchBuilder().setInPort(tunPort); addNxRegMatch(mb, RegMatch.of(NxmNxReg1.class, 0xffffffL)); Match match = mb.build(); FlowId flowId = FlowIdUtils.newFlowId(tableId, "chainport", match); FlowBuilder flow = base(tableId).setId(flowId).setMatch(match).setPriority(65000).setInstructions( instructions(applyActionIns(nxPopNshAction(), nxOutputRegAction(NxmNxReg7.class)))); return flow.build(); } private static Integer returnOfPortFromNodeConnector(NodeConnectorId nodeConnectorId) { String[] elements = StringUtils.split(nodeConnectorId.getValue(), ":"); if (elements.length != 3) return null; return new Integer(elements[2]); } /** * Get a base flow builder with some common features already set */ private static FlowBuilder base(short tableId) { return new FlowBuilder().setTableId(tableId).setBarrier(false).setHardTimeout(0).setIdleTimeout(0); } }