Merge "Bug 5214 - ARP fix for external VLAN traffic"
[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
33 import org.apache.commons.lang3.ArrayUtils;
34 import org.opendaylight.groupbasedpolicy.dto.EpKey;
35 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
36 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
37 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.l3endpoint.rev151217.NatAddress;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg1;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 /**
70  * Manage the table that assigns source endpoint group, bridge domain, and
71  * router domain to registers to be used by other tables.
72  */
73 public class IngressNatMapper extends FlowTable {
74
75     protected static final Logger LOG = LoggerFactory.getLogger(IngressNatMapper.class);
76
77     // TODO Li alagalah Improve UT coverage for this class.
78     public static short TABLE_ID;
79
80     public IngressNatMapper(OfContext ctx, short tableId) {
81         super(ctx);
82         TABLE_ID = tableId;
83     }
84
85     @Override
86     public short getTableId() {
87         return TABLE_ID;
88     }
89
90     @Override
91     public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
92
93         // TODO for consideration: default instruction is goto next table because when matching against eth type 0x8100
94         // in PortSecurity, it's not possible to match against IPv4 addresses (only inf eth type would be 0x800)
95         // We can't determine just from L2 layer if traffic should be passed from PortSecurity here to IngressNat or to
96         // SourceMapper. Various 802.1q encapsulated IPs can pass through external ports - NATed or not NATed, remote
97         // or directly connected.
98         // All external ingress traffic is currently passed here and if no match is foud - no NAT is performed
99         // and processing continues in SourceMapper.
100         Flow flow = base()
101                 .setTableId(TABLE_ID)
102                 .setPriority(1)
103                 .setInstructions(FlowUtils.instructions(FlowUtils.gotoTableIns(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER())))
104                 .setId(FlowIdUtils.newFlowId("gotoSourceMapper"))
105                 .build();
106         ofWriter.writeFlow(nodeId, TABLE_ID, flow);
107
108         // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
109
110         Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
111         Collection<EndpointL3> l3Endpoints = ctx.getEndpointManager().getL3EndpointsWithNat();
112         for (EndpointL3 l3Ep : l3Endpoints) {
113             if (l3Ep.getL2Context() != null && l3Ep.getMacAddress() !=null ) {
114                 Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
115                 if (endpointsForNode.contains(ep)) {
116                     createNatFlow(l3Ep, nodeId, ofWriter);
117                 }
118             }
119         }
120     }
121
122     private void createNatFlow(EndpointL3 l3Ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
123         NatAddress natAugL3Endpoint = l3Ep.getAugmentation(NatAddress.class);
124         // Match on L3 Nat Augmentation in Destination, set to IPAddress/Mac, send to SourceMapper
125         if (natAugL3Endpoint == null) {
126             return;
127         }
128         Endpoint ep = ctx.getEndpointManager().getEndpoint(new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
129         EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
130         if (epFwdCtxOrds == null) {
131             LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
132             return;
133         }
134         Flow flow = buildNatFlow(natAugL3Endpoint.getNatAddress(), l3Ep.getIpAddress(), l3Ep.getMacAddress(), epFwdCtxOrds);
135         if (flow != null) {
136             ofWriter.writeFlow(nodeId, TABLE_ID, flow);
137         }
138         flow = createOutsideArpFlow(l3Ep.getTenant(), natAugL3Endpoint.getNatAddress(), l3Ep.getMacAddress(), nodeId);
139         if (flow != null) {
140             ofWriter.writeFlow(nodeId, TABLE_ID, flow);
141         }
142     }
143
144     private Flow buildNatFlow(IpAddress outsideDestAddress, IpAddress insideDestAddress, MacAddress toMac,
145             EndpointFwdCtxOrdinals epFwdCtxOrds) {
146         // TODO Auto-generated method stub
147         MatchBuilder mb = new MatchBuilder();
148         Action setDestIp;
149         String outsideIpMatch;
150         Layer3Match m;
151
152         Action setDestMac = setDlDstAction(toMac);
153         FlowId flowid = new FlowId(new StringBuilder().append("IngressNat")
154             .append("|")
155             .append(outsideDestAddress)
156             .append("|")
157             .append(insideDestAddress)
158             .append("|")
159             .append(toMac)
160             .toString());
161         if (insideDestAddress.getIpv4Address() != null) {
162             setDestIp = setIpv4DstAction(insideDestAddress.getIpv4Address());
163
164             outsideIpMatch = outsideDestAddress.getIpv4Address().getValue() + "/32";
165             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(outsideIpMatch)).build();
166             mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv4)).setLayer3Match(m);
167         } else if (insideDestAddress.getIpv6Address() != null) {
168             setDestIp = setIpv6DstAction(insideDestAddress.getIpv6Address());
169             outsideIpMatch = outsideDestAddress.getIpv6Address().getValue() + "/128";
170             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(outsideIpMatch)).build();
171             mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv6)).setLayer3Match(m);
172         } else {
173             return null;
174         }
175
176         int egId = epFwdCtxOrds.getEpgId();
177         int bdId = epFwdCtxOrds.getBdId();
178         int fdId = epFwdCtxOrds.getFdId();
179         int l3Id = epFwdCtxOrds.getL3Id();
180         int cgId = epFwdCtxOrds.getCgId();
181         int tunnelId = epFwdCtxOrds.getTunnelId();
182         Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
183         Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(cgId));
184         Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
185         Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
186         Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
187         Action tunIdAction = nxLoadTunIdAction(BigInteger.valueOf(tunnelId), false);
188
189         FlowBuilder flowb = base().setPriority(Integer.valueOf(100))
190             .setId(flowid)
191             .setMatch(mb.build())
192             .setInstructions(
193                     instructions(
194                             applyActionIns(setDestIp, setDestMac, segReg, scgReg, bdReg, fdReg, vrfReg, tunIdAction),
195                             gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
196         return flowb.build();
197     }
198
199     private Flow createOutsideArpFlow(TenantId tenantId, IpAddress outsideDestAddress, MacAddress toMac, NodeId nodeId) {
200         String ikey = outsideDestAddress.getIpv4Address().getValue();
201         BigInteger intMac = new BigInteger(1, bytesFromHexString(toMac.getValue()));
202         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, ARP)).setLayer3Match(
203                 new ArpMatchBuilder().setArpOp(Integer.valueOf(1))
204                     .setArpTargetTransportAddress(new Ipv4Prefix(ikey + "/32"))
205                     .build());
206         Action[] outsideArpActions = {
207                 nxMoveEthSrcToEthDstAction(),
208                 setDlSrcAction(toMac),
209                 nxLoadArpOpAction(BigInteger.valueOf(2L)),
210                 nxMoveArpShaToArpThaAction(),
211                 nxLoadArpShaAction(intMac),
212                 nxMoveArpSpaToArpTpaAction(),
213                 nxLoadArpSpaAction(ikey),
214                 outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT"))
215         };
216         Subnet extSubnet = ExternalMapper.resolveSubnetForIpv4Address(ctx.getTenant(tenantId),
217                 outsideDestAddress.getIpv4Address());
218         L2FloodDomain l2Fd = null;
219         if (extSubnet != null && extSubnet.getParent() != null) {
220             l2Fd = ctx.getTenant(tenantId).resolveL2FloodDomain(extSubnet.getParent());
221         }
222         FlowBuilder flowb = base().setPriority(150);
223         if (l2Fd != null && l2Fd.getAugmentation(Segmentation.class) != null) {
224             Integer vlanId = l2Fd.getAugmentation(Segmentation.class).getSegmentationId();
225             mb.setVlanMatch(FlowUtils.vlanMatch(0, false));
226             Action[] pushVlanpActions = {FlowUtils.pushVlanAction(), FlowUtils.setVlanId(vlanId)};
227             flowb.setInstructions(instructions(FlowUtils.applyActionIns(ArrayUtils.addAll(
228                     pushVlanpActions,
229                     outsideArpActions))));
230         } else {
231             flowb.setInstructions(instructions(FlowUtils.applyActionIns(outsideArpActions)));
232         }
233         flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "outside-ip-arp", mb.build()));
234         flowb.setMatch(mb.build());
235         return flowb.build();
236     }
237
238     static byte[] bytesFromHexString(String values) {
239         String target = "";
240         if (values != null) {
241             target = values;
242         }
243         String[] octets = target.split(":");
244
245         byte[] ret = new byte[octets.length];
246         for (int i = 0; i < octets.length; i++) {
247             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
248         }
249         return ret;
250     }
251 }