2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
11 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ARP;
12 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
13 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ethernetMatch;
14 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
15 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
16 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpOpAction;
17 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpShaAction;
18 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpSpaAction;
19 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadRegAction;
20 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction;
21 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpShaToArpThaAction;
22 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpSpaToArpTpaAction;
23 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveEthSrcToEthDstAction;
24 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction;
25 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlDstAction;
26 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlSrcAction;
27 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setIpv4DstAction;
28 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setIpv6DstAction;
30 import java.math.BigInteger;
31 import java.util.Collection;
32 import java.util.Collections;
35 import org.apache.commons.lang3.ArrayUtils;
36 import org.opendaylight.groupbasedpolicy.dto.EgKey;
37 import org.opendaylight.groupbasedpolicy.dto.EpKey;
38 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
39 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
40 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.l3endpoint.rev151217.NatAddress;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.move.grouping.NxRegMove;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
75 import com.google.common.collect.Sets;
78 * <h1>Manage the table processing NAT translation (table=1)</h1>
80 * Ingress NAT translation flows, created for every L3 endpoints with NAT which also contain L2 context
82 * <i>Nat flow:</i><br>
85 * - nw_dst (destination ip address)<br>
87 * - loadReg0 {@link NxmNxReg0}<br>
88 * - loadReg1 {@link NxmNxReg1}<br>
89 * - loadReg4 {@link NxmNxReg4}<br>
90 * - loadReg5 {@link NxmNxReg5}<br>
91 * - loadReg6 {@link NxmNxReg6}<br>
93 * - {@link GoToTable} DESTINATION MAPPER table
95 * <i>Outside Arp flow:</i><br>
98 * - arp, (ethertype)<br>
99 * - set arp target transport address<br>
101 * - move eth_src = eth_dst {@link NxRegMove}<br>
102 * - set dl src_mac {@link MacAddress}<br>
103 * - load arp_op {@link NxRegLoad}<br>
104 * - move arp_sha = arp_tha {@link NxRegMove}<br>
105 * - load arp_sha {@link NxRegLoad}<br>
106 * - move arp_spa = arp_tpa {@link NxRegMove}<br>
107 * - load arp_spa {@link NxRegLoad}<br>
108 * - output:port {@link NodeConnectorId}
110 * Flows for ingress traffic. Created for every external endpoint without location<br>
112 * <i>Ingress external IP flow</i><br>
115 * - nw_src (source ip address)<br>
117 * - loadReg0 {@link NxmNxReg0}<br>
118 * - loadReg1 {@link NxmNxReg1}<br>
119 * - loadReg4 {@link NxmNxReg4}<br>
120 * - loadReg5 {@link NxmNxReg5}<br>
121 * - loadReg6 {@link NxmNxReg6}<br>
123 * - {@link GoToTable} DESTINATION MAPPER table
125 * <i>Ingress external Arp flow</i><br>
128 * - arp_spa (source arp address)<br>
130 * - loadReg0 {@link NxmNxReg0}<br>
131 * - loadReg1 {@link NxmNxReg1}<br>
132 * - loadReg4 {@link NxmNxReg4}<br>
133 * - loadReg5 {@link NxmNxReg5}<br>
134 * - loadReg6 {@link NxmNxReg6}<br>
136 * - {@link GoToTable} DESTINATION MAPPER table
139 public class IngressNatMapper extends FlowTable {
141 protected static final Logger LOG = LoggerFactory.getLogger(IngressNatMapper.class);
143 // TODO Li alagalah Improve UT coverage for this class.
144 public static short TABLE_ID;
146 public IngressNatMapper(OfContext ctx, short tableId) {
152 public short getTableId() {
157 public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
160 * To support provider networks, all external ingress traffic is currently passed here and
161 * if no match is foud - no NAT is performed and processing continues in DestinationMapper.
164 .setTableId(TABLE_ID)
167 FlowUtils.instructions(FlowUtils.gotoTableIns(
168 ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())))
169 .setId(FlowIdUtils.newFlowId("gotoDestinationMapper"))
171 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
173 // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
175 // Flows for ingress NAT translation
176 Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
177 Collection<EndpointL3> l3Endpoints = ctx.getEndpointManager().getL3EndpointsWithNat();
178 for (EndpointL3 l3Ep : l3Endpoints) {
179 if (l3Ep.getL2Context() != null && l3Ep.getMacAddress() !=null ) {
180 Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
181 if (endpointsForNode.contains(ep)) {
182 createNatFlow(l3Ep, nodeId, ofWriter);
186 //Flows for ingress traffic that does not have to be translated.
187 // TODO similar loop in DestinationMapper
188 for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
189 for (EgKey egKey : ctx.getEndpointManager().getEgKeysForEndpoint(ep)) {
190 Set<EgKey> groups = ctx.getCurrentPolicy().getPeers(egKey);
191 for (EgKey peer : Sets.union(Collections.singleton(egKey), ctx.getCurrentPolicy().getPeers(egKey))) {
192 for (Endpoint extEp : ctx.getEndpointManager().getExtEpsNoLocForGroup(peer)) {
193 createIngressExternalFlows(extEp, nodeId, ofWriter);
200 private void createNatFlow(EndpointL3 l3Ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
201 NatAddress natAugL3Endpoint = l3Ep.getAugmentation(NatAddress.class);
202 // Match on L3 Nat Augmentation in Destination, set to IPAddress/Mac, send to SourceMapper
203 if (natAugL3Endpoint == null) {
206 Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
207 EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
208 if (epFwdCtxOrds == null) {
209 LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
212 Flow flow = buildNatFlow(natAugL3Endpoint.getNatAddress(), l3Ep.getIpAddress(), l3Ep.getMacAddress(), epFwdCtxOrds);
214 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
216 flow = createOutsideArpFlow(l3Ep.getTenant(), natAugL3Endpoint.getNatAddress(), l3Ep.getMacAddress(), nodeId);
218 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
222 private void createIngressExternalFlows(Endpoint ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
223 EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
224 if (epFwdCtxOrds == null) {
225 LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
228 if (ep.getL3Address() != null) {
229 for (L3Address l3Addr : ep.getL3Address()) {
230 Flow ipFlow = buildIngressExternalIpFlow(l3Addr.getIpAddress(), epFwdCtxOrds);
231 if (ipFlow != null) {
232 ofWriter.writeFlow(nodeId, TABLE_ID, ipFlow);
236 Flow arpFlow = buildIngressExternalArpFlow(ep.getMacAddress(), epFwdCtxOrds);
237 if (arpFlow != null) {
238 ofWriter.writeFlow(nodeId, TABLE_ID, arpFlow);
242 private Flow buildNatFlow(IpAddress outsideDestAddress, IpAddress insideDestAddress, MacAddress toMac,
243 EndpointFwdCtxOrdinals epFwdCtxOrds) {
245 Action setDestMac = setDlDstAction(toMac);
246 FlowId flowid = new FlowId(new StringBuilder().append("IngressNat")
248 .append(outsideDestAddress)
250 .append(insideDestAddress)
254 if (insideDestAddress.getIpv4Address() != null) {
255 setDestIp = setIpv4DstAction(insideDestAddress.getIpv4Address());
256 } else if (insideDestAddress.getIpv6Address() != null) {
257 setDestIp = setIpv6DstAction(insideDestAddress.getIpv6Address());
261 MatchBuilder mb = createMatchOnDstIpAddress(outsideDestAddress);
262 Action[] dstIpMacAction = {setDestIp, setDestMac};
263 FlowBuilder flowb = base().setPriority(100)
265 .setMatch(mb.build())
268 applyActionIns(ArrayUtils.addAll(dstIpMacAction, createEpFwdCtxActions(epFwdCtxOrds))),
269 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
270 return flowb.build();
273 private Flow createOutsideArpFlow(TenantId tenantId, IpAddress outsideDestAddress, MacAddress toMac, NodeId nodeId) {
274 String ikey = outsideDestAddress.getIpv4Address().getValue();
275 BigInteger intMac = new BigInteger(1, bytesFromHexString(toMac.getValue()));
276 MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, ARP)).setLayer3Match(
277 new ArpMatchBuilder().setArpOp(1)
278 .setArpTargetTransportAddress(new Ipv4Prefix(ikey + "/32"))
280 Action[] outsideArpActions = {
281 nxMoveEthSrcToEthDstAction(),
282 setDlSrcAction(toMac),
283 nxLoadArpOpAction(BigInteger.valueOf(2L)),
284 nxMoveArpShaToArpThaAction(),
285 nxLoadArpShaAction(intMac),
286 nxMoveArpSpaToArpTpaAction(),
287 nxLoadArpSpaAction(ikey),
288 outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT"))
290 Subnet extSubnet = ExternalMapper.resolveSubnetForIpv4Address(ctx.getTenant(tenantId),
291 outsideDestAddress.getIpv4Address());
292 L2FloodDomain l2Fd = null;
293 if (extSubnet != null && extSubnet.getParent() != null) {
294 l2Fd = ctx.getTenant(tenantId).resolveL2FloodDomain(extSubnet.getParent());
296 FlowBuilder flowb = base().setPriority(150);
297 if (l2Fd != null && l2Fd.getAugmentation(Segmentation.class) != null) {
298 Integer vlanId = l2Fd.getAugmentation(Segmentation.class).getSegmentationId();
299 mb.setVlanMatch(FlowUtils.vlanMatch(0, false));
300 Action[] pushVlanpActions = {FlowUtils.pushVlanAction(), FlowUtils.setVlanId(vlanId)};
301 flowb.setInstructions(instructions(FlowUtils.applyActionIns(ArrayUtils.addAll(
303 outsideArpActions))));
305 flowb.setInstructions(instructions(FlowUtils.applyActionIns(outsideArpActions)));
307 flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "outside-ip-arp", mb.build()));
308 flowb.setMatch(mb.build());
309 return flowb.build();
313 * Builds flow for inbound IP traffic of registered external endpoint.
314 * Priority should be lower than in NAT flow.
316 private Flow buildIngressExternalIpFlow(IpAddress srcIpAddress, EndpointFwdCtxOrdinals epFwdCtxOrds) {
317 MatchBuilder mb = createMatchOnSrcIpAddress(srcIpAddress);
321 FlowBuilder flowb = base().setPriority(90)
322 .setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-ip", mb.build()))
323 .setMatch(mb.build())
325 instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
326 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
327 return flowb.build();
331 * @param srcIpAddress can be IPv4 or IPv6
332 * @return {@link MatchBuilder} with specified L2 ethertype and L3 source address.
333 * Returns null if srcIpAddress is null.
335 private MatchBuilder createMatchOnSrcIpAddress(IpAddress srcIpAddress) {
336 return createMatchOnIpAddress(srcIpAddress, true);
339 private MatchBuilder createMatchOnDstIpAddress(IpAddress srcIpAddress) {
340 return createMatchOnIpAddress(srcIpAddress, false);
343 // use createMatchOnSrcIpAddress or createMatchOnDstIpAddress
344 private MatchBuilder createMatchOnIpAddress(IpAddress srcIpAddress, boolean isSourceAddress) {
345 MatchBuilder mb = new MatchBuilder();
348 if (srcIpAddress.getIpv4Address() != null) {
349 ipPrefix = srcIpAddress.getIpv4Address().getValue() + "/32";
350 m = (isSourceAddress) ? new Ipv4MatchBuilder().setIpv4Source(new Ipv4Prefix(ipPrefix)).build() :
351 new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ipPrefix)).build();
352 mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv4))
355 } else if (srcIpAddress.getIpv6Address() != null) {
356 ipPrefix = srcIpAddress.getIpv6Address().getValue() + "/128";
357 m = (isSourceAddress) ? new Ipv6MatchBuilder().setIpv6Source(new Ipv6Prefix(ipPrefix)).build() :
358 new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ipPrefix)).build();
359 mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv6))
368 * Builds flow for inbound ARP traffic of registered external endpoint.
369 * Priority should be lower than in ARP flow for NAT address.
371 private Flow buildIngressExternalArpFlow(MacAddress srcMac, EndpointFwdCtxOrdinals epFwdCtxOrds) {
372 if (srcMac == null) {
375 MatchBuilder mb = new MatchBuilder()
376 .setEthernetMatch(ethernetMatch(srcMac, null, ARP));
378 // new ArpMatchBuilder()
379 // .setArpOp(Integer.valueOf(2))
381 FlowBuilder flowb = base().setPriority(80);
382 flowb.setInstructions(instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
383 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
384 flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-arp", mb.build()));
385 flowb.setMatch(mb.build());
386 return flowb.build();
389 private Action[] createEpFwdCtxActions(EndpointFwdCtxOrdinals epFwdCtxOrds) {
390 int egId = epFwdCtxOrds.getEpgId();
391 int bdId = epFwdCtxOrds.getBdId();
392 int fdId = epFwdCtxOrds.getFdId();
393 int l3Id = epFwdCtxOrds.getL3Id();
394 int cgId = epFwdCtxOrds.getCgId();
395 int tunnelId = epFwdCtxOrds.getTunnelId();
396 Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
397 Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(cgId));
398 Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
399 Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
400 Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
401 Action tunIdAction = nxLoadTunIdAction(BigInteger.valueOf(tunnelId), false);
402 return new Action[]{segReg, scgReg, bdReg, fdReg, vrfReg, tunIdAction};
405 static byte[] bytesFromHexString(String values) {
407 if (values != null) {
410 String[] octets = target.split(":");
412 byte[] ret = new byte[octets.length];
413 for (int i = 0; i < octets.length; i++) {
414 ret[i] = Integer.valueOf(octets[i], 16).byteValue();