Bug 3244 - SFC Improvements for distributed classifier, robustness
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / DestinationMapper.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 com.google.common.base.Optional;
12 import com.google.common.base.Strings;
13 import com.google.common.collect.HashMultimap;
14 import com.google.common.collect.SetMultimap;
15 import com.google.common.collect.Sets;
16 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.groupbasedpolicy.endpoint.EpKey;
19 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
20 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.FlowMap;
21 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
22 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory.EndpointFwdCtxOrdinals;
23 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
24 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
25 import org.opendaylight.groupbasedpolicy.resolver.TenantUtils;
26 import org.opendaylight.groupbasedpolicy.util.IidFactory;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.l3.prefix.fields.EndpointL3Gateways;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Prefix;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
73 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
76
77 import java.math.BigInteger;
78 import java.util.ArrayList;
79 import java.util.Collection;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.HashSet;
83 import java.util.List;
84 import java.util.Map;
85 import java.util.Map.Entry;
86 import java.util.Objects;
87 import java.util.Set;
88
89 import static com.google.common.base.Preconditions.checkNotNull;
90 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ARP;
91 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.IPv4;
92 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.IPv6;
93 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.addNxRegMatch;
94 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.applyActionIns;
95 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.createNodePath;
96 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.decNwTtlAction;
97 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.ethernetMatch;
98 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.getOfPortNum;
99 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
100 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.groupAction;
101 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.instructions;
102 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpOpAction;
103 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpShaAction;
104 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadArpSpaAction;
105 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadRegAction;
106 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIPv4Action;
107 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxLoadTunIdAction;
108 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpShaToArpThaAction;
109 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveArpSpaToArpTpaAction;
110 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxMoveEthSrcToEthDstAction;
111 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.outputAction;
112 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlDstAction;
113 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.setDlSrcAction;
114 import static org.opendaylight.groupbasedpolicy.util.DataStoreHelper.readFromDs;
115
116 /**
117  * Manage the table that maps the destination address to the next hop for the
118  * path as well as applies any relevant routing transformations.
119  */
120 public class DestinationMapper extends FlowTable {
121
122     protected static final Logger LOG = LoggerFactory.getLogger(DestinationMapper.class);
123
124     // TODO Li alagalah: Improve UT coverage for this class.
125
126     // TODO Li alagalah: Use EndpointL3 for L3 flows, Endpoint for L2 flows
127     // This ensures we have the appropriate network-containment'
128
129     public static short TABLE_ID;
130     /**
131      * This is the MAC address of the magical router in the sky
132      */
133     public static final MacAddress ROUTER_MAC = new MacAddress("88:f0:31:b5:12:b5");
134     public static final MacAddress MULTICAST_MAC = new MacAddress("01:00:00:00:00:00");
135     public static final Integer BASE_L3_PRIORITY = 100;
136
137     public DestinationMapper(OfContext ctx, short tableId) {
138         super(ctx);
139         this.TABLE_ID = tableId;
140     }
141
142     Map<TenantId, HashSet<Subnet>> subnetsByTenant = new HashMap<TenantId, HashSet<Subnet>>();
143
144     @Override
145     public short getTableId() {
146         return TABLE_ID;
147     }
148
149     @Override
150     public void sync(NodeId nodeId, PolicyInfo policyInfo, FlowMap flowMap) throws Exception {
151
152         TenantId currentTenant = null;
153
154         flowMap.writeFlow(nodeId, TABLE_ID, dropFlow(Integer.valueOf(1), null, TABLE_ID));
155
156         SetMultimap<EpKey, EpKey> visitedEps = HashMultimap.create();
157         Set<EndpointFwdCtxOrdinals> epOrdSet = new HashSet<>();
158
159         for (Endpoint srcEp : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
160             Set<EndpointGroupId> srcEpgIds = new HashSet<>();
161             if (srcEp.getEndpointGroup() != null)
162                 srcEpgIds.add(srcEp.getEndpointGroup());
163             if (srcEp.getEndpointGroups() != null)
164                 srcEpgIds.addAll(srcEp.getEndpointGroups());
165
166             for (EndpointGroupId epgId : srcEpgIds) {
167                 EgKey epg = new EgKey(srcEp.getTenant(), epgId);
168                 Set<EgKey> peers = Sets.union(Collections.singleton(epg), policyInfo.getPeers(epg));
169                 for (EgKey peer : peers) {
170                     for (Endpoint peerEp : ctx.getEndpointManager().getEndpointsForGroup(peer)) {
171                         currentTenant = peerEp.getTenant();
172                         subnetsByTenant.put(currentTenant, getSubnets(currentTenant));
173                         EpKey srcEpKey = new EpKey(srcEp.getL2Context(), srcEp.getMacAddress());
174                         EpKey peerEpKey = new EpKey(peerEp.getL2Context(), peerEp.getMacAddress());
175
176                         if (visitedEps.get(srcEpKey) != null && visitedEps.get(srcEpKey).contains(peerEpKey)) {
177                             continue;
178                         }
179                         syncEP(flowMap, nodeId, policyInfo, srcEp, peerEp);
180                         visitedEps.put(srcEpKey, peerEpKey);
181
182                         // Process subnets and flood-domains for epPeer
183                         EndpointFwdCtxOrdinals epOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo,
184                                 peerEp);
185                         epOrdSet.add(epOrds);
186                     }
187                 }
188             }
189         }
190
191         for (Entry<TenantId, HashSet<Subnet>> subnetEntry : subnetsByTenant.entrySet()) {
192             if (subnetEntry.getValue() == null) {
193                 LOG.trace("Tenant: {} has empty subnet entry.", subnetEntry.getKey());
194                 continue;
195             }
196             currentTenant = subnetEntry.getKey();
197             for (Subnet sn : subnetEntry.getValue()) {
198                 L3Context l3c = getL3ContextForSubnet(currentTenant, sn);
199                 Flow arpFlow = createRouterArpFlow(currentTenant, nodeId, sn,
200                         OrdinalFactory.getContextOrdinal(currentTenant, l3c.getId()));
201                 if (arpFlow != null) {
202                     flowMap.writeFlow(nodeId, TABLE_ID, arpFlow);
203                 } else {
204                     LOG.debug(
205                             "Gateway ARP flow is not created, because virtual router IP has not been set for subnet {} .",
206                             sn.getIpPrefix().getValue());
207                 }
208             }
209         }
210
211         // Write broadcast flows per flood domain.
212         for (EndpointFwdCtxOrdinals epOrd : epOrdSet) {
213             if (groupExists(nodeId, epOrd.getFdId())) {
214                 flowMap.writeFlow(nodeId, TABLE_ID, createBroadcastFlow(epOrd));
215             }
216         }
217
218         // L3 Prefix Endpoint handling
219         Collection<EndpointL3Prefix> prefixEps = ctx.getEndpointManager().getEndpointsL3PrefixForTenant(currentTenant);
220         if (prefixEps != null) {
221             LOG.trace("DestinationMapper - Processing L3PrefixEndpoints");
222             for (EndpointL3Prefix prefixEp : prefixEps) {
223                 Flow prefixFlow = createL3PrefixFlow(prefixEp, policyInfo, nodeId);
224                 if (prefixFlow != null) {
225                     flowMap.writeFlow(nodeId, TABLE_ID, prefixFlow);
226                     LOG.trace("Wrote L3Prefix flow");
227                 }
228             }
229         }
230
231     }
232
233     // set up next-hop destinations for all the endpoints in the endpoint
234     // group on the node
235
236     private Flow createL3PrefixFlow(EndpointL3Prefix prefixEp, PolicyInfo policyInfo, NodeId nodeId) throws Exception {
237         /*
238          * Priority: 100+lengthprefix
239          * Match: prefix, l3c, "mac address of router" ?
240          * Action:
241          * - set Reg2, Reg3 for L3Ep by L2Ep ?
242          * - if external,
243          * - Reg7: use switch location external port else punt for now
244          * - if internal
245          * - Reg7: grab L2Ep from L3Ep and use its location info
246          * - goto_table: POLENF (will check there for external on EP)
247          */
248
249         ReadOnlyTransaction rTx = ctx.getDataBroker().newReadOnlyTransaction();
250         // TODO Bug #3440 Target: Be - should support for more than first gateway.
251         EndpointL3Gateways l3Gateway = prefixEp.getEndpointL3Gateways().get(0);
252         Optional<EndpointL3> optL3Ep = readFromDs(LogicalDatastoreType.OPERATIONAL,
253                 IidFactory.l3EndpointIid(l3Gateway.getL3Context(), l3Gateway.getIpAddress()), rTx);
254         if (!optL3Ep.isPresent()) {
255             LOG.error("createL3PrefixFlow - L3Endpoint gateway {} for L3Prefix {} not found.", l3Gateway, prefixEp);
256             return null;
257         }
258         EndpointL3 l3Ep = optL3Ep.get();
259         Optional<Endpoint> optL2Ep = readFromDs(LogicalDatastoreType.OPERATIONAL,
260                 IidFactory.endpointIid(l3Ep.getL2Context(), l3Ep.getMacAddress()), rTx);
261         if (!optL2Ep.isPresent()) {
262             LOG.error("createL3PrefixFlow - L2Endpoint for L3Gateway {} not found.", l3Ep);
263             return null;
264         }
265         Endpoint l2Ep = optL2Ep.get();
266         EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, l2Ep);
267
268         NetworkDomainId epNetworkContainment = getEPNetworkContainment(l2Ep);
269
270         MacAddress epDestMac = l2Ep.getMacAddress();
271         MacAddress destSubnetGatewayMac = l2Ep.getMacAddress();
272
273         ArrayList<Instruction> l3instructions = new ArrayList<>();
274         List<Action> applyActions = new ArrayList<>();
275         List<Action> l3ApplyActions = new ArrayList<>();
276
277         int order = 0;
278
279         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
280         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
281         Action setNextHop;
282         String nextHop=null;
283
284         OfOverlayContext ofc = l2Ep.getAugmentation(OfOverlayContext.class);
285         LocationType location;
286
287         if (ofc != null && ofc.getLocationType() != null) {
288             location = ofc.getLocationType();
289         } else if (ofc != null) {
290             // Augmentation, but using default location
291             location = LocationType.Internal;
292         } else {
293             LOG.info("createL3PrefixFlow - Endpoint {} had no augmentation.", l2Ep);
294             return null;
295         }
296
297         long portNum = -1;
298
299         if (location.equals(LocationType.Internal)) {
300             checkNotNull(ofc.getNodeConnectorId());
301             nextHop = ofc.getNodeConnectorId().getValue();
302             try {
303                 portNum = getOfPortNum(ofc.getNodeConnectorId());
304             } catch (NumberFormatException ex) {
305                 LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
306                 return null;
307             }
308
309         } else {
310             // External
311             Set<NodeConnectorId> externalPorts = ctx.getSwitchManager().getExternalPorts(nodeId);
312             checkNotNull(externalPorts);
313             for (NodeConnectorId externalPort : externalPorts) {
314                 // TODO Bug #3440 Target: Be - should support for more than first external port.
315                 //TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
316                 nextHop = externalPort.getValue();
317                 try {
318                     portNum = getOfPortNum(externalPort);
319                 } catch (NumberFormatException ex) {
320                     LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
321                     return null;
322                 }
323                 continue;
324             }
325         }
326
327         if (Strings.isNullOrEmpty(nextHop)
328                 || portNum == -1) {
329             LOG.error("createL3Prefix - Cannot find nodeConnectorId for {} for Prefix: ", l2Ep, prefixEp);
330             return null;
331         }
332         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
333
334         Action setDlDst = setDlDstAction(epDestMac);
335         l3ApplyActions.add(setDlDst);
336
337         Action decTtl = decNwTtlAction();
338         l3ApplyActions.add(decTtl);
339
340         order += 1;
341         applyActions.add(setdEPG);
342         applyActions.add(setdCG);
343         applyActions.add(setNextHop);
344
345         applyActions.addAll(l3ApplyActions);
346         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
347             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
348             .build();
349
350         l3instructions.add(applyActionsIns);
351         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
352             .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
353             .build();
354         l3instructions.add(gotoTable);
355
356         Layer3Match m = null;
357         Long etherType = null;
358         String ikey = null;
359         Integer prefixLength=0;
360         if (prefixEp.getIpPrefix().getIpv4Prefix() != null) {
361             ikey = prefixEp.getIpPrefix().getIpv4Prefix().getValue();
362             etherType = IPv4;
363             prefixLength=Integer.valueOf(prefixEp.getIpPrefix().getIpv4Prefix().getValue().split("/")[1]);
364             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
365         } else if (prefixEp.getIpPrefix().getIpv6Prefix() != null) {
366             ikey = prefixEp.getIpPrefix().getIpv6Prefix().getValue();
367             etherType = IPv6;
368             /*
369              *  This will result in flows with priority between 100-228, but since its matching on IPv6 prefix as well
370              *  this shouldn't pose and issue, as the priority is more important within the address space of the matcher,
371              *  even though technically flows are processed in priority order.
372              */
373
374             prefixLength=Integer.valueOf(prefixEp.getIpPrefix().getIpv6Prefix().getValue().split("/")[1]);
375             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
376         } else {
377             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", prefixEp);
378             return null;
379         }
380
381         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, etherType));
382         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(epFwdCtxOrds.getL3Id())));
383         Match match = mb.build();
384         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3prefix", match);
385         FlowBuilder flowb = base().setId(flowid)
386             .setPriority(Integer.valueOf(BASE_L3_PRIORITY+prefixLength))
387             .setMatch(match)
388             .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
389         return flowb.build();
390     }
391
392     private Flow createBroadcastFlow(EndpointFwdCtxOrdinals epOrd) {
393         MatchBuilder mb = new MatchBuilder()
394                             .setEthernetMatch(new EthernetMatchBuilder()
395                             .setEthernetDestination(new EthernetDestinationBuilder().
396                                                         setAddress(MULTICAST_MAC)
397                                                         .setMask(MULTICAST_MAC).build())
398                             .build());
399         addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class, Long.valueOf(epOrd.getFdId())));
400
401         Match match = mb.build();
402         FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "broadcast", match);
403         FlowBuilder flowb = base().setPriority(Integer.valueOf(140))
404             .setId(flowId)
405             .setMatch(match)
406             .setInstructions(
407                     instructions(applyActionIns(nxLoadTunIdAction(BigInteger.valueOf(epOrd.getFdId()), false),
408                             groupAction(Long.valueOf(epOrd.getFdId())))));
409
410         return flowb.build();
411     }
412
413     private boolean groupExists(NodeId nodeId, Integer fdId) throws Exception {
414         // Fetch existing GroupTables
415         if (ctx.getDataBroker() == null) {
416             return false;
417         }
418
419         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
420         InstanceIdentifier<Node> niid = createNodePath(nodeId);
421         Optional<Node> r = t.read(LogicalDatastoreType.CONFIGURATION, niid).get();
422         if (!r.isPresent())
423             return false;
424         FlowCapableNode fcn = r.get().getAugmentation(FlowCapableNode.class);
425         if (fcn == null)
426             return false;
427
428         if (fcn.getGroup() != null) {
429             for (Group g : fcn.getGroup()) {
430                 if (g.getGroupId().getValue().equals(Long.valueOf(fdId))) { // Group
431                                                                             // Exists.
432                     return true;
433                 }
434             }
435         }
436         return false;
437     }
438
439     private MacAddress routerPortMac(L3Context l3c, IpAddress ipAddress) {
440
441         if (ctx.getDataBroker() == null) {
442             return null;
443         }
444
445         MacAddress defaultMacAddress = ROUTER_MAC;
446
447         EndpointL3Key l3Key = new EndpointL3Key(ipAddress, l3c.getId());
448         InstanceIdentifier<EndpointL3> endpointsIid = InstanceIdentifier.builder(Endpoints.class)
449             .child(EndpointL3.class, l3Key)
450             .build();
451         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
452
453         Optional<EndpointL3> r;
454         try {
455             r = t.read(LogicalDatastoreType.OPERATIONAL, endpointsIid).get();
456             if (!r.isPresent())
457                 return defaultMacAddress;
458             EndpointL3 epL3 = r.get();
459             if (epL3.getMacAddress() == null) {
460                 return defaultMacAddress;
461             } else {
462                 return epL3.getMacAddress();
463             }
464         } catch (Exception e) {
465             LOG.error("Error reading EndpointL3 {}.{}", l3c, ipAddress, e);
466             return null;
467         }
468     }
469
470     private L3Context getL3ContextForSubnet(TenantId tenantId, Subnet sn) {
471         L3Context l3c = ctx.getPolicyResolver().getTenant(tenantId).resolveL3Context(sn.getId());
472         return l3c;
473     }
474
475     private Flow createRouterArpFlow(TenantId tenantId, NodeId nodeId, Subnet sn, int l3Id) {
476         if (sn == null || sn.getVirtualRouterIp() == null) {
477             LOG.trace("Didn't create routerArpFlow since either subnet or subnet virtual router was null");
478             return null;
479         }
480         /*
481          * TODO: Li alagalah: This should be new Yang "gateways" list as well,
482          * that expresses the gateway and prefixes it is interface for. Should
483          * also check for external.
484          */
485         if (sn.getVirtualRouterIp().getIpv4Address() != null) {
486             String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue();
487
488             L3Context l3c = getL3ContextForSubnet(tenantId, sn);
489             if (l3c == null) {
490                 LOG.error("No L3 Context found associated with subnet {}", sn.getId());
491             }
492
493             MacAddress routerMac = routerPortMac(l3c, sn.getVirtualRouterIp());
494             if (routerMac == null) {
495                 return null;
496             }
497
498             BigInteger intRouterMac = new BigInteger(1, bytesFromHexString(routerMac.getValue()));
499
500             MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, null, ARP)).setLayer3Match(
501                     new ArpMatchBuilder().setArpOp(Integer.valueOf(1))
502                         .setArpTargetTransportAddress(new Ipv4Prefix(ikey + "/32"))
503                         .build());
504             addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(l3Id)));
505
506             Match match = mb.build();
507             FlowId flowId = FlowIdUtils.newFlowId(TABLE_ID, "routerarp", match);
508             FlowBuilder flowb = base().setPriority(150)
509                 .setId(flowId)
510                 .setMatch(match)
511                 .setInstructions(
512                         instructions(applyActionIns(nxMoveEthSrcToEthDstAction(), setDlSrcAction(routerMac),
513                                 nxLoadArpOpAction(BigInteger.valueOf(2L)), nxMoveArpShaToArpThaAction(),
514                                 nxLoadArpShaAction(intRouterMac), nxMoveArpSpaToArpTpaAction(),
515                                 nxLoadArpSpaAction(ikey), outputAction(new NodeConnectorId(nodeId.getValue()
516                                         + ":INPORT")))));
517             return flowb.build();
518         } else {
519             LOG.warn("IPv6 virtual router {} for subnet {} not supported", sn.getVirtualRouterIp(), sn.getId()
520                 .getValue());
521             return null;
522         }
523
524     }
525
526     private Flow createLocalL2Flow(Endpoint ep, EndpointFwdCtxOrdinals epFwdCtxOrds, OfOverlayContext ofc) {
527
528         // TODO Li alagalah - refactor common code but keep simple method
529         ArrayList<Instruction> instructions = new ArrayList<>();
530         List<Action> applyActions = new ArrayList<>();
531
532         int order = 0;
533
534         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
535         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
536         Action setNextHop;
537         String nextHop;
538
539         // BEGIN L2 LOCAL
540         nextHop = ofc.getNodeConnectorId().getValue();
541
542         long portNum;
543         try {
544             portNum = getOfPortNum(ofc.getNodeConnectorId());
545         } catch (NumberFormatException ex) {
546             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
547             return null;
548         }
549
550         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
551
552         // END L2 LOCAL
553
554         order += 1;
555         applyActions.add(setdEPG);
556         applyActions.add(setdCG);
557         applyActions.add(setNextHop);
558         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
559             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
560             .build();
561         instructions.add(applyActionsIns);
562
563         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
564             .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
565             .build();
566         instructions.add(gotoTable);
567
568         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, ep.getMacAddress(), null));
569         addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(epFwdCtxOrds.getBdId())));
570         Match match = mb.build();
571         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "localL2", match);
572         FlowBuilder flowb = base().setId(flowid)
573             .setPriority(Integer.valueOf(50))
574             .setMatch(match)
575             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
576         return flowb.build();
577     }
578
579     private void syncEP(FlowMap flowMap, NodeId nodeId, PolicyInfo policyInfo, Endpoint srcEp, Endpoint destEp)
580             throws Exception {
581
582         // TODO: Conditions messed up, but for now, send policyInfo until this
583         // is fixed.
584         EndpointFwdCtxOrdinals destEpFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, destEp);
585         EndpointFwdCtxOrdinals srcEpFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, policyInfo, srcEp);
586
587         if (destEp.getTenant() == null || (destEp.getEndpointGroup() == null && destEp.getEndpointGroups() == null)) {
588             LOG.trace("Didn't process endpoint due to either tenant, or EPG(s) being null", destEp.getKey());
589             return;
590         }
591         OfOverlayContext ofc = destEp.getAugmentation(OfOverlayContext.class);
592
593         if (LocationType.External.equals(ofc.getLocationType())) {
594             LOG.error("syncEp(): External endpoints should not be seen here.");
595             return;
596         }
597
598         /*
599          * Only care about subnets for L3, but fetch them before loop. We need
600          * the local subnets for setting SRC MAC for routing. All Routing is now
601          * done locally! YAY! Instead of being shovelled L2 style across network
602          * ala Helium.
603          */
604         List<Subnet> localSubnets = getLocalSubnets(nodeId);
605         if (localSubnets == null) {
606             LOG.error("No subnets could be found locally for node: {}", nodeId);
607             return;
608         }
609
610         if (Objects.equals(ofc.getNodeId(), nodeId)) {
611             // this is a local endpoint; send to the approppriate local
612             // port
613
614             if (srcEpFwdCtxOrds.getBdId() == destEpFwdCtxOrds.getBdId()) {
615                 flowMap.writeFlow(nodeId, TABLE_ID, createLocalL2Flow(destEp, destEpFwdCtxOrds, ofc));
616             }
617             // TODO Li alagalah: Need to move to EndpointL3 for L3 processing.
618             // The Endpoint conflation must end!
619             if (destEp.getL3Address() == null) {
620                 LOG.trace("Endpoint {} didn't have L3 Address so was not processed for L3 flows.", destEp.getKey());
621                 return;
622             }
623
624             for (L3Address l3a : destEp.getL3Address()) {
625                 if (l3a.getIpAddress() == null || l3a.getL3Context() == null) {
626                     LOG.error("Endpoint with L3Address but either IPAddress or L3Context is null. {}",
627                             destEp.getL3Address());
628                     continue;
629                 } else {
630                     for (Subnet localSubnet : localSubnets) {
631                         Flow flow = createLocalL3RoutedFlow(destEp, l3a, destEpFwdCtxOrds, ofc, localSubnet);
632                         if (flow != null) {
633                             flowMap.writeFlow(nodeId, TABLE_ID, flow);
634                         } else {
635                             LOG.trace("Did not write remote L3 flow for endpoint {} and subnet {}", l3a.getIpAddress(),
636                                     localSubnet.getIpPrefix().getValue());
637                         }
638                     }
639                 }
640             }
641         } else {
642             // this endpoint is on a different switch; send to the
643             // appropriate tunnel
644             if (srcEpFwdCtxOrds.getBdId() == destEpFwdCtxOrds.getBdId()) {
645                 Flow remoteL2Flow = createRemoteL2Flow(destEp, nodeId, srcEpFwdCtxOrds, destEpFwdCtxOrds, ofc);
646                 if (remoteL2Flow != null) {
647                     flowMap.writeFlow(nodeId, TABLE_ID, remoteL2Flow);
648                 }
649             } else {
650                 LOG.trace("DestinationMapper: RemoteL2Flow: not created, in different BDs src: {} dst: {}",
651                         srcEpFwdCtxOrds.getBdId(), destEpFwdCtxOrds.getBdId());
652             }
653
654             // TODO Li alagalah: Need to move to EndpointL3 for L3 processing.
655             // The Endpoint conflation must end!
656             if (destEp.getL3Address() == null) {
657                 LOG.trace("Endpoint {} didn't have L3 Address so was not processed for L3 flows.", destEp.getKey());
658                 return;
659             }
660             for (L3Address l3a : destEp.getL3Address()) {
661                 if (l3a.getIpAddress() == null || l3a.getL3Context() == null) {
662                     LOG.error("Endpoint with L3Address but either IPAddress or L3Context is null. {}",
663                             destEp.getL3Address());
664                     continue;
665                 } else {
666                     for (Subnet localSubnet : localSubnets) {
667                         Flow remoteL3Flow = createRemoteL3RoutedFlow(destEp, l3a, nodeId, srcEpFwdCtxOrds,
668                                 destEpFwdCtxOrds, ofc, localSubnet);
669                         if (remoteL3Flow != null) {
670                             flowMap.writeFlow(nodeId, TABLE_ID, remoteL3Flow);
671                         } else {
672                             LOG.trace("Did not write remote L3 flow for endpoint {} and subnet {}", l3a.getIpAddress(),
673                                     localSubnet.getIpPrefix().getValue());
674                         }
675                     }
676                 }
677             }
678         } // remote (tunnel)
679
680         // }
681
682     }
683
684     /*
685      * ################################## DestMapper Flow methods
686      * ##################################
687      */
688     private Flow createLocalL3RoutedFlow(Endpoint destEp, L3Address destL3Address, EndpointFwdCtxOrdinals epFwdCtxOrds,
689             OfOverlayContext ofc, Subnet srcSubnet) {
690
691         // TODO Li alagalah - refactor common code but keep simple method
692
693         Subnet destSubnet = null;
694         HashSet<Subnet> subnets = getSubnets(destEp.getTenant());
695         if (subnets == null) {
696             LOG.trace("No subnets in tenant {}", destEp.getTenant());
697             return null;
698         }
699         NetworkDomainId epNetworkContainment = getEPNetworkContainment(destEp);
700         for (Subnet subnet : subnets) {
701             // TODO Li alagalah add IPv6 support
702             if (subnet.getId().getValue().equals(epNetworkContainment.getValue())) {
703                 destSubnet = subnet;
704                 break;
705             }
706         }
707         if (destSubnet == null) {
708             LOG.trace("Destination IP address does not match any subnet in tenant {}", destL3Address.getIpAddress());
709             return null;
710         }
711
712         if (destSubnet.getVirtualRouterIp() == null) {
713             LOG.trace("Destination subnet {} for Endpoint {}.{} has no gateway IP", destSubnet.getIpPrefix(),
714                     destL3Address.getKey());
715             return null;
716         }
717
718         if (srcSubnet.getVirtualRouterIp() == null) {
719             LOG.trace("Local subnet {} has no gateway IP", srcSubnet.getIpPrefix());
720             return null;
721         }
722         L3Context destL3c = getL3ContextForSubnet(destEp.getTenant(), destSubnet);
723         if (destL3c == null || destL3c.getId() == null) {
724             LOG.error("No L3 Context found associated with subnet {}", destSubnet.getId());
725             return null;
726         }
727         L3Context srcL3c = getL3ContextForSubnet(destEp.getTenant(), srcSubnet);
728         if (srcL3c == null || srcL3c.getId() == null) {
729             LOG.error("No L3 Context found associated with subnet {}", srcSubnet.getId());
730             return null;
731         }
732
733         if (!(srcL3c.getId().getValue().equals(destL3c.getId().getValue()))) {
734             LOG.trace("Trying to route between two L3Contexts {} and {}. Not currently supported.", srcL3c.getId()
735                 .getValue(), destL3c.getId().getValue());
736             return null;
737         }
738
739         MacAddress matcherMac = routerPortMac(destL3c, srcSubnet.getVirtualRouterIp());
740         MacAddress epDestMac = destEp.getMacAddress();
741         MacAddress destSubnetGatewayMac = routerPortMac(destL3c, destSubnet.getVirtualRouterIp());
742
743         if (srcSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
744             // This is our final destination, so match on actual EP mac.
745             matcherMac = epDestMac;
746         }
747
748         ArrayList<Instruction> l3instructions = new ArrayList<>();
749         List<Action> applyActions = new ArrayList<>();
750         List<Action> l3ApplyActions = new ArrayList<>();
751
752         int order = 0;
753
754         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
755         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
756         Action setNextHop;
757         String nextHop;
758
759         // BEGIN L3 LOCAL
760         nextHop = ofc.getNodeConnectorId().getValue();
761
762         long portNum;
763         try {
764             portNum = getOfPortNum(ofc.getNodeConnectorId());
765         } catch (NumberFormatException ex) {
766             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
767             return null;
768         }
769
770         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
771         // END L3 LOCAL
772
773         // Lets not re-write the srcMac if its local.
774         if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
775             Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
776             l3ApplyActions.add(setDlSrc);
777         }
778
779         Action setDlDst = setDlDstAction(epDestMac);
780         l3ApplyActions.add(setDlDst);
781
782         Action decTtl = decNwTtlAction();
783         l3ApplyActions.add(decTtl);
784
785         order += 1;
786         applyActions.add(setdEPG);
787         applyActions.add(setdCG);
788         applyActions.add(setNextHop);
789
790         applyActions.addAll(l3ApplyActions);
791         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
792             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
793             .build();
794
795         l3instructions.add(applyActionsIns);
796         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
797             .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
798             .build();
799         l3instructions.add(gotoTable);
800         Layer3Match m = null;
801         Long etherType = null;
802         String ikey = null;
803         if (destL3Address.getIpAddress().getIpv4Address() != null) {
804             ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
805             etherType = IPv4;
806             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
807         } else if (destL3Address.getIpAddress().getIpv6Address() != null) {
808             ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
809             etherType = IPv6;
810             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
811         } else {
812             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", destL3Address.toString());
813             return null;
814         }
815
816         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
817             .setLayer3Match(m);
818         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(epFwdCtxOrds.getL3Id())));
819         Match match = mb.build();
820         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "localL3", match);
821         FlowBuilder flowb = base().setId(flowid)
822             .setPriority(Integer.valueOf(132))
823             .setMatch(match)
824             .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
825         return flowb.build();
826     }
827
828     private Flow createRemoteL2Flow(Endpoint ep, NodeId nodeId, EndpointFwdCtxOrdinals srcEpFwdCtxOrds,
829             EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc) {
830
831         // TODO Li alagalah - refactor common code but keep simple method
832
833         // this endpoint is on a different switch; send to the
834         // appropriate tunnel
835
836         ArrayList<Instruction> instructions = new ArrayList<>();
837         List<Action> applyActions = new ArrayList<>();
838
839         int order = 0;
840
841         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(destEpFwdCtxOrds.getEpgId()));
842         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(destEpFwdCtxOrds.getCgId()));
843         Action setNextHop;
844         String nextHop;
845
846         // BEGIN TUNNEL HANDLING
847         IpAddress tunDst = ctx.getSwitchManager().getTunnelIP(ofc.getNodeId(), TunnelTypeVxlan.class);
848         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
849         if (tunDst == null) {
850             LOG.warn("Failed to get Tunnel IP for NodeId {} with EP {}", nodeId, ep);
851             return null;
852         }
853         if (tunPort == null) {
854             LOG.warn("Failed to get Tunnel Port for NodeId {} with EP {}", nodeId, ep);
855             return null;
856         }
857
858         Action tundstAction;
859
860         if (tunDst.getIpv4Address() != null) {
861             nextHop = tunDst.getIpv4Address().getValue();
862             tundstAction = nxLoadTunIPv4Action(nextHop, false);
863         } else if (tunDst.getIpv6Address() != null) {
864             // nextHop = tunDst.getIpv6Address().getValue();
865             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
866                     ofc.getNodeId());
867             return null;
868         } else {
869             // this shouldn't happen
870             LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
871             return null;
872         }
873
874         long portNum;
875         try {
876             portNum = getOfPortNum(tunPort);
877         } catch (NumberFormatException ex) {
878             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
879             return null;
880         }
881
882         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
883         applyActions.add(tundstAction);
884         // END TUNNEL
885
886         order += 1;
887         applyActions.add(setdEPG);
888         applyActions.add(setdCG);
889         applyActions.add(setNextHop);
890         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
891             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
892             .build();
893         instructions.add(applyActionsIns);
894
895         applyActionsIns = new InstructionBuilder().setOrder(order++)
896             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
897             .build();
898
899         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
900             .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
901             .build();
902         instructions.add(gotoTable);
903
904         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, ep.getMacAddress(), null));
905         addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(destEpFwdCtxOrds.getBdId())));
906         Match match = mb.build();
907         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "remoteL2", match);
908         FlowBuilder flowb = base().setId(flowid)
909             .setPriority(Integer.valueOf(50))
910             .setMatch(match)
911             .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
912
913         return flowb.build();
914     }
915
916     private Flow createRemoteL3RoutedFlow(Endpoint destEp, L3Address destL3Address, NodeId nodeId,
917             EndpointFwdCtxOrdinals srcEpFwdCtxOrds, EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc,
918             Subnet srcSubnet) {
919
920         // TODO Li alagalah - refactor common code but keep simple method
921
922         // this endpoint is on a different switch; send to the
923         // appropriate tunnel
924         Subnet destSubnet = null;
925         HashSet<Subnet> subnets = getSubnets(destEp.getTenant());
926         if (subnets == null) {
927             LOG.trace("No subnets in tenant {}", destEp.getTenant());
928             return null;
929         }
930         NetworkDomainId epNetworkContainment = getEPNetworkContainment(destEp);
931         for (Subnet subnet : subnets) {
932             // TODO Li alagalah add IPv6 support
933             if (subnet.getId().getValue().equals(epNetworkContainment.getValue())) {
934                 destSubnet = subnet;
935                 break;
936             }
937         }
938         if (destSubnet == null) {
939             LOG.info("Destination IP address does not match any subnet in tenant {}", destL3Address.getIpAddress());
940             return null;
941         }
942
943         if (destSubnet.getVirtualRouterIp() == null) {
944             LOG.trace("Destination subnet {} for Endpoint {}.{} has no gateway IP", destSubnet.getIpPrefix(),
945                     destL3Address.getKey());
946             return null;
947         }
948
949         if (srcSubnet.getVirtualRouterIp() == null) {
950             LOG.trace("Local subnet {} has no gateway IP", srcSubnet.getIpPrefix());
951             return null;
952         }
953         L3Context destL3c = getL3ContextForSubnet(destEp.getTenant(), destSubnet);
954         if (destL3c == null || destL3c.getId() == null) {
955             LOG.error("No L3 Context found associated with subnet {}", destSubnet.getId());
956             return null;
957         }
958         L3Context srcL3c = getL3ContextForSubnet(destEp.getTenant(), srcSubnet);
959         if (srcL3c == null || srcL3c.getId() == null) {
960             LOG.error("No L3 Context found associated with subnet {}", srcSubnet.getId());
961             return null;
962         }
963
964         if (!(srcL3c.getId().getValue().equals(destL3c.getId().getValue()))) {
965             LOG.trace("Trying to route between two L3Contexts {} and {}. Not currently supported.", srcL3c.getId()
966                 .getValue(), destL3c.getId().getValue());
967             return null;
968         }
969
970         MacAddress matcherMac = routerPortMac(destL3c, srcSubnet.getVirtualRouterIp());
971         MacAddress epDestMac = destEp.getMacAddress();
972         MacAddress destSubnetGatewayMac = routerPortMac(destL3c, destSubnet.getVirtualRouterIp());
973
974         ArrayList<Instruction> l3instructions = new ArrayList<>();
975         List<Action> applyActions = new ArrayList<>();
976         List<Action> l3ApplyActions = new ArrayList<>();
977
978         int order = 0;
979
980         Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(destEpFwdCtxOrds.getEpgId()));
981         Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(destEpFwdCtxOrds.getCgId()));
982         Action setNextHop;
983         String nextHop;
984
985         // BEGIN TUNNEL HANDLING
986         IpAddress tunDst = ctx.getSwitchManager().getTunnelIP(ofc.getNodeId(), TunnelTypeVxlan.class);
987         NodeConnectorId tunPort = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
988         if (tunDst == null) {
989             LOG.warn("Failed to get Tunnel IP for NodeId {} with L3Address {}", nodeId, destL3Address);
990             return null;
991         }
992         if (tunPort == null) {
993             LOG.warn("Failed to get Tunnel port for NodeId {} with L3Address {}", nodeId, destL3Address);
994             return null;
995         }
996
997         Action tundstAction;
998
999         if (tunDst.getIpv4Address() != null) {
1000             nextHop = tunDst.getIpv4Address().getValue();
1001             tundstAction = nxLoadTunIPv4Action(nextHop, false);
1002         } else if (tunDst.getIpv6Address() != null) {
1003             // nextHop = tunDst.getIpv6Address().getValue();
1004             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
1005                     ofc.getNodeId());
1006             return null;
1007         } else {
1008             // this shouldn't happen
1009             LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
1010             return null;
1011         }
1012
1013         long portNum;
1014         try {
1015             portNum = getOfPortNum(tunPort);
1016         } catch (NumberFormatException ex) {
1017             LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex);
1018             return null;
1019         }
1020
1021         setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
1022         applyActions.add(tundstAction);
1023         // END TUNNEL
1024
1025         order += 1;
1026         applyActions.add(setdEPG);
1027         applyActions.add(setdCG);
1028         applyActions.add(setNextHop);
1029
1030         Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
1031         l3ApplyActions.add(setDlSrc);
1032
1033         Action setDlDst = setDlDstAction(epDestMac);
1034         l3ApplyActions.add(setDlDst);
1035
1036         Action decTtl = decNwTtlAction();
1037         l3ApplyActions.add(decTtl);
1038
1039         applyActions.addAll(l3ApplyActions);
1040         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
1041             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
1042             .build();
1043
1044         l3instructions.add(applyActionsIns);
1045         Instruction gotoTable = new InstructionBuilder().setOrder(order++)
1046             .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
1047             .build();
1048         l3instructions.add(gotoTable);
1049         Layer3Match m = null;
1050         Long etherType = null;
1051         String ikey = null;
1052         if (destL3Address.getIpAddress().getIpv4Address() != null) {
1053             ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
1054             etherType = IPv4;
1055             m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
1056         } else if (destL3Address.getIpAddress().getIpv6Address() != null) {
1057             ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
1058             etherType = IPv6;
1059             m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
1060         } else {
1061             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", destL3Address.toString());
1062             return null;
1063         }
1064
1065         MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
1066             .setLayer3Match(m);
1067         addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(destEpFwdCtxOrds.getL3Id())));
1068         Match match = mb.build();
1069         FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "remoteL3", match);
1070         FlowBuilder flowb = base().setId(flowid)
1071             .setPriority(Integer.valueOf(132))
1072             .setMatch(match)
1073             .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
1074         return flowb.build();
1075     }
1076
1077     private NetworkDomainId getEPNetworkContainment(Endpoint endpoint) {
1078         if (endpoint.getNetworkContainment() != null) {
1079             return endpoint.getNetworkContainment();
1080         } else {
1081             /*
1082              * TODO: Be alagalah: Endpoint Refactor: This should be set on input
1083              * which we can't do because of the backwards way endpoints were
1084              * "architected".
1085              */
1086             return ctx.getPolicyResolver()
1087                 .getTenant(endpoint.getTenant())
1088                 .getEndpointGroup(endpoint.getEndpointGroup())
1089                 .getNetworkDomain();
1090         }
1091     }
1092
1093     private HashSet<Subnet> getSubnets(final TenantId tenantId) {
1094
1095         // if (subnetsByTenant.get(tenantId) != null) {
1096         // return subnetsByTenant.get(tenantId);
1097         // }
1098
1099         if (ctx.getDataBroker() == null) {
1100             return null;
1101         }
1102
1103         ReadOnlyTransaction t = ctx.getDataBroker().newReadOnlyTransaction();
1104         InstanceIdentifier<Tenant> tiid = TenantUtils.tenantIid(tenantId);
1105         Optional<Tenant> tenantInfo;
1106         try {
1107             tenantInfo = t.read(LogicalDatastoreType.CONFIGURATION, tiid).get();
1108         } catch (Exception e) {
1109             LOG.error("Could not read Tenant {}", tenantId, e);
1110             return null;
1111         }
1112
1113         HashSet<Subnet> subnets = new HashSet<Subnet>();
1114
1115         if (!tenantInfo.isPresent()) {
1116             LOG.warn("Tenant {} not found", tenantId);
1117             return null;
1118         }
1119
1120         subnets.addAll(tenantInfo.get().getSubnet());
1121         // subnetsByTenant.put(tenantId, subnets);
1122         return subnets;
1123     }
1124
1125     // Need a method to get subnets for EPs attached to the node locally
1126     // to set the source Mac address for the router interface.
1127     private List<Subnet> getLocalSubnets(NodeId nodeId) {
1128         Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
1129
1130         List<Subnet> localSubnets = new ArrayList<Subnet>();
1131
1132         for (Endpoint endpoint : endpointsForNode) {
1133             HashSet<Subnet> subnets = getSubnets(endpoint.getTenant());
1134             if (subnets == null) {
1135                 LOG.error("No local subnets.");
1136                 return null;
1137             }
1138             NetworkDomainId epNetworkContainment = getEPNetworkContainment(endpoint);
1139             for (Subnet subnet : subnets) {
1140                 if (epNetworkContainment.getValue().equals(subnet.getId().getValue())) {
1141                     localSubnets.add(subnet);
1142                 }
1143             }
1144         }
1145         return localSubnets;
1146     }
1147
1148     static byte[] bytesFromHexString(String values) {
1149         String target = "";
1150         if (values != null) {
1151             target = values;
1152         }
1153         String[] octets = target.split(":");
1154
1155         byte[] ret = new byte[octets.length];
1156         for (int i = 0; i < octets.length; i++) {
1157             ret[i] = Integer.valueOf(octets[i], 16).byteValue();
1158         }
1159         return ret;
1160     }
1161 }