29c6dffaec53bd10c14bbbb26aa87f4cf1a7f002
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / IngressNatMapper.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
10
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;
29
30 import java.math.BigInteger;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Set;
34
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;
75
76 import com.google.common.base.Preconditions;
77 import com.google.common.collect.Sets;
78
79 /**
80  * Manage the table that assigns source endpoint group, bridge domain, and
81  * router domain to registers to be used by other tables.
82  */
83 public class IngressNatMapper extends FlowTable {
84
85     protected static final Logger LOG = LoggerFactory.getLogger(IngressNatMapper.class);
86
87     // TODO Li alagalah Improve UT coverage for this class.
88     public static short TABLE_ID;
89
90     public IngressNatMapper(OfContext ctx, short tableId) {
91         super(ctx);
92         TABLE_ID = tableId;
93     }
94
95     @Override
96     public short getTableId() {
97         return TABLE_ID;
98     }
99
100     @Override
101     public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
102
103         /**
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.
106          */
107         Flow flow = base()
108                 .setTableId(TABLE_ID)
109                 .setPriority(1)
110             .setInstructions(
111                     FlowUtils.instructions(FlowUtils.gotoTableIns(
112                         ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())))
113                 .setId(FlowIdUtils.newFlowId("gotoDestinationMapper"))
114                 .build();
115         ofWriter.writeFlow(nodeId, TABLE_ID, flow);
116
117         // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
118
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);
127                 }
128             }
129         }
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);
138                     }
139                 }
140             }
141         }
142     }
143
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) {
148             return;
149         }
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);
154             return;
155         }
156         Flow flow = buildNatFlow(natAugL3Endpoint.getNatAddress(), l3Ep.getIpAddress(), l3Ep.getMacAddress(), epFwdCtxOrds);
157         if (flow != null) {
158             ofWriter.writeFlow(nodeId, TABLE_ID, flow);
159         }
160         flow = createOutsideArpFlow(l3Ep.getTenant(), natAugL3Endpoint.getNatAddress(), l3Ep.getMacAddress(), nodeId);
161         if (flow != null) {
162             ofWriter.writeFlow(nodeId, TABLE_ID, flow);
163         }
164     }
165
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);
170             return;
171         }
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);
177                 }
178             }
179         }
180         Flow arpFlow = buildIngressExternalArpFlow(ep.getMacAddress(), epFwdCtxOrds);
181         if (arpFlow != null) {
182             ofWriter.writeFlow(nodeId, TABLE_ID, arpFlow);
183         }
184     }
185
186     private Flow buildNatFlow(IpAddress outsideDestAddress, IpAddress insideDestAddress, MacAddress toMac,
187             EndpointFwdCtxOrdinals epFwdCtxOrds) {
188         Action setDestIp;
189         Action setDestMac = setDlDstAction(toMac);
190         FlowId flowid = new FlowId(new StringBuilder().append("IngressNat")
191             .append("|")
192             .append(outsideDestAddress)
193             .append("|")
194             .append(insideDestAddress)
195             .append("|")
196             .append(toMac)
197             .toString());
198         if (insideDestAddress.getIpv4Address() != null) {
199             setDestIp = setIpv4DstAction(insideDestAddress.getIpv4Address());
200         } else if (insideDestAddress.getIpv6Address() != null) {
201             setDestIp = setIpv6DstAction(insideDestAddress.getIpv6Address());
202         } else {
203             return null;
204         }
205         MatchBuilder mb = createMatchOnDstIpAddress(outsideDestAddress);
206         Action[] dstIpMacAction = {setDestIp, setDestMac};
207         FlowBuilder flowb = base().setPriority(Integer.valueOf(100))
208             .setId(flowid)
209             .setMatch(mb.build())
210             .setInstructions(
211                     instructions(
212                             applyActionIns(ArrayUtils.addAll(dstIpMacAction, createEpFwdCtxActions(epFwdCtxOrds))),
213                             gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
214         return flowb.build();
215     }
216
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"))
223                     .build());
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"))
233         };
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());
239         }
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(
246                     pushVlanpActions,
247                     outsideArpActions))));
248         } else {
249             flowb.setInstructions(instructions(FlowUtils.applyActionIns(outsideArpActions)));
250         }
251         flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "outside-ip-arp", mb.build()));
252         flowb.setMatch(mb.build());
253         return flowb.build();
254     }
255
256     /**
257      * Builds flow for inbound IP traffic of registered external endpoint.
258      * Priority should be lower than in NAT flow.
259      */
260     private Flow buildIngressExternalIpFlow(IpAddress srcIpAddress, EndpointFwdCtxOrdinals epFwdCtxOrds) {
261         MatchBuilder mb = createMatchOnSrcIpAddress(srcIpAddress);
262         if (mb == null) {
263             return null;
264         }
265         FlowBuilder flowb = base().setPriority(Integer.valueOf(90))
266             .setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-ip", mb.build()))
267             .setMatch(mb.build())
268             .setInstructions(
269                     instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
270                             gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
271         return flowb.build();
272     }
273
274     /**
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.
278      */
279     private MatchBuilder createMatchOnSrcIpAddress(IpAddress srcIpAddress) {
280         return createMatchOnIpAddress(srcIpAddress, true);
281     }
282
283     private MatchBuilder createMatchOnDstIpAddress(IpAddress srcIpAddress) {
284         return createMatchOnIpAddress(srcIpAddress, false);
285     }
286
287     // use createMatchOnSrcIpAddress or createMatchOnDstIpAddress
288     private MatchBuilder createMatchOnIpAddress(IpAddress srcIpAddress, boolean isSourceAddress) {
289         MatchBuilder mb = new MatchBuilder();
290         String ipPrefix;
291         Layer3Match m;
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))
297               .setLayer3Match(m);
298             return mb;
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))
304               .setLayer3Match(m);
305             return mb;
306         } else {
307             return null;
308         }
309     }
310
311     /**
312      * Builds flow for inbound ARP traffic of registered external endpoint.
313      * Priority should be lower than in ARP flow for NAT address.
314      */
315     private Flow buildIngressExternalArpFlow(MacAddress srcMac, EndpointFwdCtxOrdinals epFwdCtxOrds) {
316         if (srcMac == null) {
317             return null;
318         }
319         MatchBuilder mb = new MatchBuilder()
320             .setEthernetMatch(ethernetMatch(srcMac, null, ARP));
321             //.setLayer3Match(
322             //        new ArpMatchBuilder()
323            //             .setArpOp(Integer.valueOf(2))
324            //             .build());
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();
331     }
332
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;
348     }
349
350     static byte[] bytesFromHexString(String values) {
351         String target = "";
352         if (values != null) {
353             target = values;
354         }
355         String[] octets = target.split(":");
356
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();
360         }
361         return ret;
362     }
363 }