/*
* 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.destination;
import com.google.common.base.Preconditions;
import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowIdUtils;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory;
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.action.types.rev131112.action.Action;
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.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.instruction.go.to.table._case.GoToTable;
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.common.rev140421.TenantId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
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.endpoint.rev140421.endpoints.EndpointL3Prefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Prefix;
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.L3Context;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
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.ethernet.match.fields.EthernetDestinationBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
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.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.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
class DestinationMapperFlows {
private static final Logger LOG = LoggerFactory.getLogger(DestinationMapperFlows.class);
private final DestinationMapperUtils utils;
private final NodeId nodeId;
private final short tableId;
public DestinationMapperFlows(DestinationMapperUtils utils, NodeId nodeId, short tableId) {
this.utils = Preconditions.checkNotNull(utils);
this.nodeId = Preconditions.checkNotNull(nodeId);
this.tableId = 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());
}
/**
* Create external L2 flow for every external port found on node
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param peerEndpoint original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
* @param externalPorts list of external {@link NodeConnectorId}-s get from node
* @param ofWriter flow writer
*/
void createExternalL2Flow(short goToTable, int priority, Endpoint peerEndpoint, Set<NodeConnectorId> externalPorts,
OfWriter ofWriter) {
OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals = utils.getEndpointOrdinals(peerEndpoint);
if (peerOrdinals != null) {
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(ethernetMatch(null, peerEndpoint.getMacAddress(), null));
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) peerOrdinals.getBdId()));
Match match = matchBuilder.build();
long port;
for (NodeConnectorId externalPort : externalPorts) {
try {
port = getOfPortNum(externalPort);
writeExternalL2Flow(goToTable, priority, peerOrdinals, port, match, ofWriter);
} catch (NumberFormatException e) {
LOG.warn("Invalid NodeConnectorId. External port: {}", externalPort);
}
}
}
}
/**
* Create external L3 flow for every external port found on node
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param peerEndpoint to original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
* @param l2GatewayEp L2 endpoint of subnet gateway
* @param destL3Address endpoint L3 address
* @param externalPorts list of external {@link NodeConnectorId}-s get from node
* @param ofWriter flow writer
*/
void createExternalL3RoutedFlow(short goToTable, int priority, Endpoint peerEndpoint, Endpoint l2GatewayEp,
L3Address destL3Address, Set<NodeConnectorId> externalPorts, OfWriter ofWriter) {
OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals = utils.getEndpointOrdinals(peerEndpoint);
if (peerOrdinals != null) {
Layer3Match layer3Match;
Long etherType;
String ikey;
if (destL3Address.getIpAddress() != null && destL3Address.getIpAddress().getIpv4Address() != null) {
ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
etherType = IPv4;
layer3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
} else if (destL3Address.getIpAddress() != null && destL3Address.getIpAddress().getIpv6Address() != null) {
ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
etherType = IPv6;
layer3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
} else {
LOG.error("Endpoint has Ip Address that is not recognised as either IPv4 or IPv6.", destL3Address);
return;
}
MacAddress matcherMac = peerEndpoint.getMacAddress();
MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
.setLayer3Match(layer3Match);
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) peerOrdinals.getL3Id()));
Match match = matchBuilder.build();
long port;
for (NodeConnectorId externalPort : externalPorts) {
try {
port = getOfPortNum(externalPort);
writeExternalL3RoutedFlow(goToTable, priority, port, l2GatewayEp, match, peerOrdinals, ofWriter);
} catch (NumberFormatException e) {
LOG.warn("Invalid NodeConnectorId. External port: {}", externalPort);
}
}
}
}
/**
* Create local L2 flow
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param endpoint original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
* @param ofWriter flow writer
*/
void createLocalL2Flow(short goToTable, int priority, Endpoint endpoint, OfWriter ofWriter) {
OfOverlayContext context = endpoint.getAugmentation(OfOverlayContext.class);
OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
Action setNextHop;
try {
setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(getOfPortNum(context.getNodeConnectorId())));
} catch (NumberFormatException ex) {
LOG.warn("Could not parse port number {}", context.getNodeConnectorId(), ex);
return;
}
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
applyActions.add(setNextHop);
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> instructions = new ArrayList<>();
instructions.add(applyActionsIns);
instructions.add(gotoTable);
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(ethernetMatch(null, endpoint.getMacAddress(), null));
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) ordinals.getBdId()));
Match match = matchBuilder.build();
FlowId flowId = FlowIdUtils.newFlowId(tableId, "localL2", match);
FlowBuilder flowBuilder = base(tableId).setId(flowId)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Create local L3 routed flow
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param endpoint original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
* @param l3Address endpoint L3 address
* @param localSubnet subnet from local node
* @param destSubnet destination endpoint's subnet
* @param ofWriter flow writer
*/
void createLocalL3RoutedFlow(short goToTable, int priority, Endpoint endpoint, L3Address l3Address,
Subnet localSubnet, Subnet destSubnet, OfWriter ofWriter) {
NodeConnectorId connectorId = endpoint.getAugmentation(OfOverlayContext.class).getNodeConnectorId();
L3Context l3Context = utils.getL3ContextForSubnet(utils.getIndexedTenant(endpoint.getTenant()), localSubnet);
if (l3Context == null) {
return;
}
MacAddress matcherMac = utils.routerPortMac(l3Context, localSubnet.getVirtualRouterIp(), endpoint.getTenant());
MacAddress epDestMac = endpoint.getMacAddress();
if (matcherMac == null || epDestMac == null) {
return;
}
MacAddress destSubnetGatewayMac = utils.routerPortMac(l3Context, destSubnet.getVirtualRouterIp(),
endpoint.getTenant());
OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
if (localSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
matcherMac = epDestMac;
}
Action setNextHopAction;
try {
setNextHopAction = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(getOfPortNum(connectorId)));
} catch (NumberFormatException ex) {
LOG.warn("Could not parse port number {}", connectorId, ex);
return;
}
List<Action> l3ApplyActions = new ArrayList<>();
l3ApplyActions.add(setDlDstAction(epDestMac));
l3ApplyActions.add(decNwTtlAction());
if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
l3ApplyActions.add(setDlSrcAction(destSubnetGatewayMac));
}
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
applyActions.add(setNextHopAction);
applyActions.addAll(l3ApplyActions);
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> l3instructions = new ArrayList<>();
l3instructions.add(applyActionsIns);
l3instructions.add(gotoTable);
Layer3Match l3Match;
Long etherType;
String ikey;
if (l3Address.getIpAddress() != null && l3Address.getIpAddress().getIpv4Address() != null) {
ikey = l3Address.getIpAddress().getIpv4Address().getValue() + "/32";
etherType = IPv4;
l3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
} else if (l3Address.getIpAddress() != null && l3Address.getIpAddress().getIpv6Address() != null) {
ikey = l3Address.getIpAddress().getIpv6Address().getValue() + "/128";
etherType = IPv6;
l3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
} else {
LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", l3Address.toString());
return;
}
MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
.setLayer3Match(l3Match);
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
Match match = matchBuilder.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "localL3", match);
FlowBuilder flowBuilder = base(tableId).setId(flowid)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Create remote L2 flow
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param endpoint original peer
* @param peerEndpoint peer endpoint to original endpoint
* @param tunDst tunnel destination Ip address
* @param connectorId tunnel port
* @param ofWriter flow writer
*/
void createRemoteL2Flow(short goToTable, int priority, Endpoint endpoint, Endpoint peerEndpoint, IpAddress tunDst,
NodeConnectorId connectorId, OfWriter ofWriter) {
OrdinalFactory.EndpointFwdCtxOrdinals endpointOrdinals = utils.getEndpointOrdinals(endpoint);
long port;
try {
port = getOfPortNum(connectorId);
} catch (NumberFormatException ex) {
LOG.warn("Could not parse port number {}", connectorId);
return;
}
Action tunnelDestinationAction = null;
if (tunDst.getIpv4Address() != null) {
tunnelDestinationAction = nxLoadTunIPv4Action(tunDst.getIpv4Address().getValue(), false);
} else if (tunDst.getIpv6Address() != null) {
LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
nodeId);
return;
}
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(endpointOrdinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(endpointOrdinals.getCgId())));
applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
applyActions.add(tunnelDestinationAction);
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> instructions = new ArrayList<>();
instructions.add(applyActionsIns);
instructions.add(gotoTable);
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(ethernetMatch(null, peerEndpoint.getMacAddress(), null));
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) endpointOrdinals.getBdId()));
Match match = matchBuilder.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "remoteL2", match);
FlowBuilder flowBuilder = base(tableId).setId(flowid)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Create remote L3 routed flow
*
* @param goToTable {@link GoToTable} instruction value
* @param priority of the flow
* @param endpoint peer
* @param destL3Address destination L3 address
* @param destSubnet subnet from destination node
* @param tunDst tunnel destination Ip address
* @param connectorId tunnel port
* @param localSubnet subnet from local node
* @param ofWriter flow writer
*/
void createRemoteL3RoutedFlow(short goToTable, int priority, Endpoint endpoint, L3Address destL3Address,
Subnet destSubnet, IpAddress tunDst, NodeConnectorId connectorId, Subnet localSubnet,
OfWriter ofWriter) {
L3Context context = utils.getL3ContextForSubnet(utils.getIndexedTenant(endpoint.getTenant()), destSubnet);
if (context == null) {
return;
}
OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
MacAddress matcherMac = utils.routerPortMac(context, localSubnet.getVirtualRouterIp(), endpoint.getTenant());
MacAddress epDestMac = endpoint.getMacAddress();
if (matcherMac == null || epDestMac == null) {
return;
}
MacAddress destSubnetGatewayMac = utils.routerPortMac(context, destSubnet.getVirtualRouterIp(),
endpoint.getTenant());
// L3 Actions
List<Action> l3ApplyActions = new ArrayList<>();
if (localSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
// This is our final destination, so match on actual EP mac.
matcherMac = epDestMac;
}
if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
l3ApplyActions.add(setDlSrc);
}
l3ApplyActions.add(setDlDstAction(epDestMac));
l3ApplyActions.add(decNwTtlAction());
// Actions
Action tunnelDestinationAction;
if (tunDst != null && tunDst.getIpv4Address() != null) {
tunnelDestinationAction = nxLoadTunIPv4Action(tunDst.getIpv4Address().getValue(), false);
} else if (tunDst != null && tunDst.getIpv6Address() != null) {
LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(), nodeId);
return;
} else {
LOG.error("Tunnel IP for {} invalid", nodeId);
return;
}
long port;
try {
port = getOfPortNum(connectorId);
} catch (NumberFormatException ex) {
LOG.warn("Could not parse port number {}", connectorId, ex);
return;
}
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
applyActions.add(tunnelDestinationAction);
applyActions.addAll(l3ApplyActions);
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> l3instructions = new ArrayList<>();
l3instructions.add(applyActionsIns);
l3instructions.add(gotoTable);
Layer3Match layer3Match;
Long etherType;
String ikey;
if (destL3Address.getIpAddress().getIpv4Address() != null) {
ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
etherType = IPv4;
layer3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
} else {
ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
etherType = IPv6;
layer3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
}
MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
.setLayer3Match(layer3Match);
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
Match match = matchBuilder.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "remoteL3", match);
FlowBuilder flowBuilder = base(tableId).setId(flowid)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* Creates arp flow using virtual router IP in {@link Subnet}
*
* @param priority of the flow
* @param indexedTenant of the {@link Endpoint}
* @param subnet entries get from peer's tenants
* @param ofWriter flow writer
* @throws Exception could be thrown during {@link OrdinalFactory#getContextOrdinal(TenantId, UniqueId)}. Handled
* in {@link DestinationMapper#syncArpFlow(DestinationMapperFlows, TenantId, OfWriter)}
*/
void createRouterArpFlow(int priority, IndexedTenant indexedTenant, Subnet subnet, OfWriter ofWriter)
throws Exception {
Tenant tenant = indexedTenant.getTenant();
if (tenant != null) {
L3Context l3Context = utils.getL3ContextForSubnet(indexedTenant, subnet);
if (l3Context != null) {
int contextOrdinal = OrdinalFactory.getContextOrdinal(tenant.getId(), l3Context.getId());
MacAddress routerMac = utils.routerPortMac(l3Context, subnet.getVirtualRouterIp(),
indexedTenant.getTenant().getId());
if (routerMac != null) {
if (subnet.getVirtualRouterIp().getIpv4Address() == null
&& subnet.getVirtualRouterIp().getIpv6Address() != null) {
LOG.warn("IPv6 virtual router {} for subnet {} not supported", subnet.getVirtualRouterIp(), subnet.getId()
.getValue());
return;
}
String ipv4Value = subnet.getVirtualRouterIp().getIpv4Address().getValue();
BigInteger intRouterMac = new BigInteger(1, bytesFromHexString(routerMac.getValue()));
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(ethernetMatch(null, null, ARP))
.setLayer3Match(new ArpMatchBuilder()
.setArpOp(1)
.setArpTargetTransportAddress(new Ipv4Prefix(ipv4Value + "/32"))
.build());
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) contextOrdinal));
Match match = matchBuilder.build();
FlowId flowId = FlowIdUtils.newFlowId(tableId, "routerarp", match);
FlowBuilder flowBuilder = base(tableId).setPriority(priority)
.setId(flowId)
.setMatch(match)
.setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(),
setDlSrcAction(routerMac), nxLoadArpOpAction(BigInteger.valueOf(2L)),
nxMoveArpShaToArpThaAction(), nxLoadArpShaAction(intRouterMac),
nxMoveArpSpaToArpTpaAction(), nxLoadArpSpaAction(ipv4Value),
outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT")))));
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
} else {
LOG.error("No L3 Context found associated with subnet {}.", subnet.getId());
}
}
}
/**
* Broadcast flow for destination mapper
*
* @param priority of the flow
* @param ordinals of the endpoint (input parameter in {@link DestinationMapper#sync(Endpoint, OfWriter)})
* @param mac address of the multicast router {@link DestinationMapper#MULTICAST_MAC}
* @param ofWriter flow writer
*/
void createBroadcastFlow(int priority, OrdinalFactory.EndpointFwdCtxOrdinals ordinals, MacAddress mac,
OfWriter ofWriter) {
MatchBuilder matchBuilder = new MatchBuilder()
.setEthernetMatch(new EthernetMatchBuilder()
.setEthernetDestination(new EthernetDestinationBuilder().setAddress(mac).setMask(mac).build())
.build());
addNxRegMatch(matchBuilder, FlowUtils.RegMatch.of(NxmNxReg5.class, (long) ordinals.getFdId()));
Match match = matchBuilder.build();
FlowId flowId = FlowIdUtils.newFlowId(tableId, "broadcast", match);
FlowBuilder flowBuilder = base(tableId)
.setPriority(priority)
.setId(flowId)
.setMatch(match)
.setInstructions(instructions(applyActionIns(nxLoadTunIdAction(BigInteger
.valueOf(ordinals.getFdId()), false), groupAction((long) ordinals.getFdId()))));
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
/**
* L3 prefix flow is created with endpoint {@link NodeConnectorId} if internal. If endpoint is external and
* external ports are present, one flow per external port is created
*
* @param goToTable policy enforcer table Id
* @param priority of the flow
* @param gatewayEp L2 endpoint, should contain {@link MacAddress} and {@link OrdinalFactory.EndpointFwdCtxOrdinals}
* @param l3Prefix endpoint L3 prefix value
* @param tenant value get from {@link L3Prefix}
* @param localSubnet value where this node is present
* @param externalPorts list of all external ports
* @param ofWriter flow writer
*/
void createL3PrefixFlow(short goToTable, int priority, Endpoint gatewayEp, EndpointL3Prefix l3Prefix, IndexedTenant tenant,
Subnet localSubnet, Set<NodeConnectorId> externalPorts, OfWriter ofWriter) {
L3Context l3Context = utils.getL3ContextForSubnet(tenant, localSubnet);
if (l3Context != null && localSubnet.getVirtualRouterIp() != null) {
MacAddress matcherMacAddress = utils.routerPortMac(l3Context, localSubnet.getVirtualRouterIp(),
tenant.getTenant().getId());
OfOverlayContext context = gatewayEp.getAugmentation(OfOverlayContext.class);
if (EndpointManager.isInternal(gatewayEp, tenant.getExternalImplicitGroups())) {
Preconditions.checkNotNull(context.getNodeConnectorId());
try {
Long port = getOfPortNum(context.getNodeConnectorId());
if(matcherMacAddress != null) {
writeL3PrefixFlow(priority, goToTable, gatewayEp, l3Prefix, port, matcherMacAddress, ofWriter);
}
} catch (NumberFormatException e) {
LOG.warn("Could not parse port number {}", context.getNodeConnectorId());
}
} else { // External
for (NodeConnectorId externalPort : externalPorts) {
try {
Long port = getOfPortNum(externalPort);
if(matcherMacAddress != null) {
writeL3PrefixFlow(priority, goToTable, gatewayEp, l3Prefix, port, matcherMacAddress, ofWriter);
}
} catch (NumberFormatException e) {
LOG.warn("Could not parse port number {}", externalPort);
}
}
}
}
}
private void writeExternalL2Flow(short goToTable, int priority, OrdinalFactory.EndpointFwdCtxOrdinals ordinals,
Long port, Match match, OfWriter ofWriter) {
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> instructions = new ArrayList<>();
instructions.add(applyActionsIns);
instructions.add(gotoTable);
FlowId flowId = FlowIdUtils.newFlowId(tableId, "externalL2", match);
FlowBuilder flowBuilder = base(tableId).setId(flowId)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
private void writeExternalL3RoutedFlow(short goToTable, int priority, long port, Endpoint l2GatewayEp, Match match,
OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals, OfWriter ofWriter) {
MacAddress destSubnetGatewayMac = l2GatewayEp.getMacAddress();
List<Action> l3ApplyActions = new ArrayList<>();
l3ApplyActions.add(setDlSrcAction(destSubnetGatewayMac));
l3ApplyActions.add(setDlDstAction(l2GatewayEp.getMacAddress()));
List<Action> applyActions = new ArrayList<>();
applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(peerOrdinals.getEpgId())));
applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(peerOrdinals.getCgId())));
applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
applyActions.addAll(l3ApplyActions);
int order = 0;
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
ArrayList<Instruction> l3instructions = new ArrayList<>();
l3instructions.add(applyActionsIns);
l3instructions.add(gotoTable);
FlowId flowid = FlowIdUtils.newFlowId(tableId, "externalL3", match);
FlowBuilder flowBuilder = base(tableId).setId(flowid)
.setPriority(priority)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
private void writeL3PrefixFlow(int priority, short goToTable, Endpoint endpoint, EndpointL3Prefix l3Prefix,
Long port, MacAddress matcherMacAddress, OfWriter ofWriter) {
MacAddress macAddress = endpoint.getMacAddress();
OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
Action setEpgAction = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId()));
Action setCgAction = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId()));
List<Action> l3ApplyActions = new ArrayList<>();
l3ApplyActions.add(setDlDstAction(macAddress));
l3ApplyActions.add(decNwTtlAction());
List<Action> applyActions = new ArrayList<>();
applyActions.add(setEpgAction);
applyActions.add(setCgAction);
applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
applyActions.addAll(l3ApplyActions);
int order = 0;
ArrayList<Instruction> l3instructions = new ArrayList<>();
Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
.setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
.build();
Instruction gotoTable = new InstructionBuilder().setOrder(order)
.setInstruction(gotoTableIns(goToTable))
.build();
l3instructions.add(applyActionsIns);
l3instructions.add(gotoTable);
if(l3Prefix.getIpPrefix() != null) {
Long etherType;
Integer prefixLength;
if (l3Prefix.getIpPrefix().getIpv4Prefix() != null) {
etherType = IPv4;
prefixLength = Integer.valueOf(l3Prefix.getIpPrefix().getIpv4Prefix().getValue().split("/")[1]);
} else if (l3Prefix.getIpPrefix().getIpv6Prefix() != null) {
etherType = IPv6;
prefixLength = Integer.valueOf(l3Prefix.getIpPrefix().getIpv6Prefix().getValue().split("/")[1]);
} else {
LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", l3Prefix);
return;
}
MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMacAddress, etherType));
addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
Match match = matchBuilder.build();
FlowId flowid = FlowIdUtils.newFlowId(tableId, "L3prefix", match);
FlowBuilder flowBuilder = base(tableId).setId(flowid)
.setPriority(priority + prefixLength)
.setMatch(match)
.setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
}
}
}