/* * 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.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 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.rev100924.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.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; /** *

Creation of flows related to service chain

* * 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 *

* * Flow that allows ALL traffic incoming from chain last hop *

* Allow from chain flow
* Table = 0
* Priority = 1200
* Matches:
* - Nshc1
* - Nsp
* - Nsi
* - in_port (tunnel port) {@link NodeConnectorId}
* Actions:
* - {@link GoToTable} SOURCE MAPPER table *

* TODO: looks like duplicity, the same flow is created in policy enforcer * Allow from chain tunnel * Table = 4
* Priority = 65000
* Matches:
* - in_port (tunnel port) {@link NodeConnectorId}
* - Reg7 (fixed value 0xffffff) {@link NxmNxReg7} * Actions:
* - {@link GoToTable} SOURCE MAPPER table *

* Create external flow * Table = 6
* Priority = 1000 (if dst node == src node, priority = 1500)
* Matches:
* - Reg6 {@link NxmNxReg6}
* - tunnel ID
* - nsp
* - nsi
* - tun_dst (only if dst node == src node)
* Actions:
* - set nshc1
* - set nshc2
* - load tunnel ID
* - load tunnel ipv4
* - output:(tunnel port)
*

* Chain tunnel flow
* Table = 2
* Priority = 150
* Matches:
* - in_port (tunnel port) {@link NodeConnectorId}
* - tunnel ID
* - nsp
* - nsi
* Actions:
* - Reg0 {@link NxmNxReg0}
* - Reg1 {@link NxmNxReg1}
* - Reg4 {@link NxmNxReg4}
* - Reg5 {@link NxmNxReg5}
* - Reg6 {@link NxmNxReg6}
* - {@link GoToTable} DESTINATION MAPPER table
*

* Chain broadcast flow
* Table = 2
* Priority = 150
* Matches:
* - in_port (tunnel port) {@link NodeConnectorId}
* - tunnel ID
* - nsp
* - nsi
* Actions:
* - load Reg5 {@link NxmNxReg5}
* - {@link GoToTable} DESTINATION MAPPER table
*/ public class ChainActionFlows { private static final Logger LOG = LoggerFactory.getLogger(ChainAction.class); 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); 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, outputAction))); return flowb.build(); } private static List 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 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(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); } }