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;
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.l2.types.rev130827.EtherType;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
76 import com.google.common.base.Preconditions;
77 import com.google.common.collect.Sets;
80 * Manage the table that assigns source endpoint group, bridge domain, and
81 * router domain to registers to be used by other tables.
83 public class IngressNatMapper extends FlowTable {
85 protected static final Logger LOG = LoggerFactory.getLogger(IngressNatMapper.class);
87 // TODO Li alagalah Improve UT coverage for this class.
88 public static short TABLE_ID;
90 public IngressNatMapper(OfContext ctx, short tableId) {
96 public short getTableId() {
101 public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
104 * To support provider networks, all external ingress traffic is currently passed here and
105 * if no match is foud - no NAT is performed and processing continues in DestinationMapper.
108 .setTableId(TABLE_ID)
111 FlowUtils.instructions(FlowUtils.gotoTableIns(
112 ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())))
113 .setId(FlowIdUtils.newFlowId("gotoDestinationMapper"))
115 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
117 // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
119 // Flows for ingress NAT translation
120 Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
121 Collection<EndpointL3> l3Endpoints = ctx.getEndpointManager().getL3EndpointsWithNat();
122 for (EndpointL3 l3Ep : l3Endpoints) {
123 if (l3Ep.getL2Context() != null && l3Ep.getMacAddress() !=null ) {
124 Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
125 if (endpointsForNode.contains(ep)) {
126 createNatFlow(l3Ep, nodeId, ofWriter);
130 //Flows for ingress traffic that does not have to be translated.
131 // TODO similar loop in DestinationMapper
132 for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
133 for (EgKey egKey : ctx.getEndpointManager().getEgKeysForEndpoint(ep)) {
134 Set<EgKey> groups = ctx.getCurrentPolicy().getPeers(egKey);
135 for (EgKey peer : Sets.union(Collections.singleton(egKey), ctx.getCurrentPolicy().getPeers(egKey))) {
136 for (Endpoint extEp : ctx.getEndpointManager().getExtEpsNoLocForGroup(peer)) {
137 createIngressExternalFlows(extEp, nodeId, ofWriter);
144 private void createNatFlow(EndpointL3 l3Ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
145 NatAddress natAugL3Endpoint = l3Ep.getAugmentation(NatAddress.class);
146 // Match on L3 Nat Augmentation in Destination, set to IPAddress/Mac, send to SourceMapper
147 if (natAugL3Endpoint == null) {
150 Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
151 EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
152 if (epFwdCtxOrds == null) {
153 LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
156 Flow flow = buildNatFlow(natAugL3Endpoint.getNatAddress(), l3Ep.getIpAddress(), l3Ep.getMacAddress(), epFwdCtxOrds);
158 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
160 flow = createOutsideArpFlow(l3Ep.getTenant(), natAugL3Endpoint.getNatAddress(), l3Ep.getMacAddress(), nodeId);
162 ofWriter.writeFlow(nodeId, TABLE_ID, flow);
166 private void createIngressExternalFlows(Endpoint ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
167 EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
168 if (epFwdCtxOrds == null) {
169 LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
172 if (ep.getL3Address() != null) {
173 for (L3Address l3Addr : ep.getL3Address()) {
174 Flow ipFlow = buildIngressExternalIpFlow(l3Addr.getIpAddress(), epFwdCtxOrds);
175 if (ipFlow != null) {
176 ofWriter.writeFlow(nodeId, TABLE_ID, ipFlow);
180 Flow arpFlow = buildIngressExternalArpFlow(ep.getMacAddress(), epFwdCtxOrds);
181 if (arpFlow != null) {
182 ofWriter.writeFlow(nodeId, TABLE_ID, arpFlow);
186 private Flow buildNatFlow(IpAddress outsideDestAddress, IpAddress insideDestAddress, MacAddress toMac,
187 EndpointFwdCtxOrdinals epFwdCtxOrds) {
189 Action setDestMac = setDlDstAction(toMac);
190 FlowId flowid = new FlowId(new StringBuilder().append("IngressNat")
192 .append(outsideDestAddress)
194 .append(insideDestAddress)
198 if (insideDestAddress.getIpv4Address() != null) {
199 setDestIp = setIpv4DstAction(insideDestAddress.getIpv4Address());
200 } else if (insideDestAddress.getIpv6Address() != null) {
201 setDestIp = setIpv6DstAction(insideDestAddress.getIpv6Address());
205 MatchBuilder mb = createMatchOnDstIpAddress(outsideDestAddress);
206 Action[] dstIpMacAction = {setDestIp, setDestMac};
207 FlowBuilder flowb = base().setPriority(Integer.valueOf(100))
209 .setMatch(mb.build())
212 applyActionIns(ArrayUtils.addAll(dstIpMacAction, createEpFwdCtxActions(epFwdCtxOrds))),
213 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
214 return flowb.build();
217 private Flow createOutsideArpFlow(TenantId tenantId, IpAddress outsideDestAddress, MacAddress toMac, NodeId nodeId) {
218 String ikey = outsideDestAddress.getIpv4Address().getValue();
219 BigInteger intMac = new BigInteger(1, bytesFromHexString(toMac.getValue()));
220 MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, ARP)).setLayer3Match(
221 new ArpMatchBuilder().setArpOp(Integer.valueOf(1))
222 .setArpTargetTransportAddress(new Ipv4Prefix(ikey + "/32"))
224 Action[] outsideArpActions = {
225 nxMoveEthSrcToEthDstAction(),
226 setDlSrcAction(toMac),
227 nxLoadArpOpAction(BigInteger.valueOf(2L)),
228 nxMoveArpShaToArpThaAction(),
229 nxLoadArpShaAction(intMac),
230 nxMoveArpSpaToArpTpaAction(),
231 nxLoadArpSpaAction(ikey),
232 outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT"))
234 Subnet extSubnet = ExternalMapper.resolveSubnetForIpv4Address(ctx.getTenant(tenantId),
235 outsideDestAddress.getIpv4Address());
236 L2FloodDomain l2Fd = null;
237 if (extSubnet != null && extSubnet.getParent() != null) {
238 l2Fd = ctx.getTenant(tenantId).resolveL2FloodDomain(extSubnet.getParent());
240 FlowBuilder flowb = base().setPriority(150);
241 if (l2Fd != null && l2Fd.getAugmentation(Segmentation.class) != null) {
242 Integer vlanId = l2Fd.getAugmentation(Segmentation.class).getSegmentationId();
243 mb.setVlanMatch(FlowUtils.vlanMatch(0, false));
244 Action[] pushVlanpActions = {FlowUtils.pushVlanAction(), FlowUtils.setVlanId(vlanId)};
245 flowb.setInstructions(instructions(FlowUtils.applyActionIns(ArrayUtils.addAll(
247 outsideArpActions))));
249 flowb.setInstructions(instructions(FlowUtils.applyActionIns(outsideArpActions)));
251 flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "outside-ip-arp", mb.build()));
252 flowb.setMatch(mb.build());
253 return flowb.build();
257 * Builds flow for inbound IP traffic of registered external endpoint.
258 * Priority should be lower than in NAT flow.
260 private Flow buildIngressExternalIpFlow(IpAddress srcIpAddress, EndpointFwdCtxOrdinals epFwdCtxOrds) {
261 MatchBuilder mb = createMatchOnSrcIpAddress(srcIpAddress);
265 FlowBuilder flowb = base().setPriority(Integer.valueOf(90))
266 .setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-ip", mb.build()))
267 .setMatch(mb.build())
269 instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
270 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
271 return flowb.build();
275 * @param srcIpAddress can be IPv4 or IPv6
276 * @return {@link MatchBuilder} with specified L2 ethertype and L3 source address.
277 * Returns null if srcIpAddress is null.
279 private MatchBuilder createMatchOnSrcIpAddress(IpAddress srcIpAddress) {
280 return createMatchOnIpAddress(srcIpAddress, true);
283 private MatchBuilder createMatchOnDstIpAddress(IpAddress srcIpAddress) {
284 return createMatchOnIpAddress(srcIpAddress, false);
287 // use createMatchOnSrcIpAddress or createMatchOnDstIpAddress
288 private MatchBuilder createMatchOnIpAddress(IpAddress srcIpAddress, boolean isSourceAddress) {
289 MatchBuilder mb = new MatchBuilder();
292 if (srcIpAddress.getIpv4Address() != null) {
293 ipPrefix = srcIpAddress.getIpv4Address().getValue() + "/32";
294 m = (isSourceAddress) ? new Ipv4MatchBuilder().setIpv4Source(new Ipv4Prefix(ipPrefix)).build() :
295 new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ipPrefix)).build();
296 mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv4))
299 } else if (srcIpAddress.getIpv6Address() != null) {
300 ipPrefix = srcIpAddress.getIpv6Address().getValue() + "/128";
301 m = (isSourceAddress) ? new Ipv6MatchBuilder().setIpv6Source(new Ipv6Prefix(ipPrefix)).build() :
302 new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ipPrefix)).build();
303 mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv6))
312 * Builds flow for inbound ARP traffic of registered external endpoint.
313 * Priority should be lower than in ARP flow for NAT address.
315 private Flow buildIngressExternalArpFlow(MacAddress srcMac, EndpointFwdCtxOrdinals epFwdCtxOrds) {
316 if (srcMac == null) {
319 MatchBuilder mb = new MatchBuilder()
320 .setEthernetMatch(ethernetMatch(srcMac, null, ARP));
322 // new ArpMatchBuilder()
323 // .setArpOp(Integer.valueOf(2))
325 FlowBuilder flowb = base().setPriority(80);
326 flowb.setInstructions(instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
327 gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
328 flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-arp", mb.build()));
329 flowb.setMatch(mb.build());
330 return flowb.build();
333 private Action[] createEpFwdCtxActions(EndpointFwdCtxOrdinals epFwdCtxOrds) {
334 int egId = epFwdCtxOrds.getEpgId();
335 int bdId = epFwdCtxOrds.getBdId();
336 int fdId = epFwdCtxOrds.getFdId();
337 int l3Id = epFwdCtxOrds.getL3Id();
338 int cgId = epFwdCtxOrds.getCgId();
339 int tunnelId = epFwdCtxOrds.getTunnelId();
340 Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
341 Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(cgId));
342 Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
343 Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
344 Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
345 Action tunIdAction = nxLoadTunIdAction(BigInteger.valueOf(tunnelId), false);
346 Action[] outsideArpActions = {segReg, scgReg, bdReg, fdReg, vrfReg, tunIdAction};
347 return outsideArpActions;
350 static byte[] bytesFromHexString(String values) {
352 if (values != null) {
355 String[] octets = target.split(":");
357 byte[] ret = new byte[octets.length];
358 for (int i = 0; i < octets.length; i++) {
359 ret[i] = Integer.valueOf(octets[i], 16).byteValue();