b7cea0f684c7c3da6c4491b2a57031a91ba7c0d1
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / mapper / destination / DestinationMapperFlows.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.mapper.destination;
10
11 import com.google.common.base.Preconditions;
12 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
13 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
14 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
15 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowIdUtils;
16 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils;
17 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.OrdinalFactory;
18 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
19 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.UniqueId;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Prefix;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.unregister.endpoint.input.L3Prefix;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L3Context;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import java.math.BigInteger;
59 import java.util.ArrayList;
60 import java.util.List;
61 import java.util.Set;
62
63 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
64
65 class DestinationMapperFlows {
66
67     private static final Logger LOG = LoggerFactory.getLogger(DestinationMapperFlows.class);
68     private final DestinationMapperUtils utils;
69     private final NodeId nodeId;
70     private final short tableId;
71
72     public DestinationMapperFlows(DestinationMapperUtils utils, NodeId nodeId, short tableId) {
73         this.utils = Preconditions.checkNotNull(utils);
74         this.nodeId = Preconditions.checkNotNull(nodeId);
75         this.tableId = tableId;
76     }
77
78     /**
79      * Default flow which drops all incoming traffic
80      *
81      * @param priority  of flow in the table
82      * @param etherType can be set as specific protocol to match
83      * @param ofWriter  flow writer
84      */
85     void dropFlow(int priority, Long etherType, OfWriter ofWriter) {
86         FlowId flowId;
87         FlowBuilder flowBuilder = FlowUtils.base(tableId)
88                 .setPriority(priority)
89                 .setInstructions(FlowUtils.dropInstructions());
90         if (etherType != null) {
91             MatchBuilder matchBuilder = new MatchBuilder()
92                     .setEthernetMatch(FlowUtils.ethernetMatch(null, null, etherType));
93             Match match = matchBuilder.build();
94             flowId = FlowIdUtils.newFlowId(tableId, "drop", match);
95             flowBuilder.setMatch(match);
96         } else {
97             flowId = FlowIdUtils.newFlowId("dropAll");
98         }
99         flowBuilder.setId(flowId);
100         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
101     }
102
103     /**
104      * Create external L2 flow for every external port found on node
105      *
106      * @param goToTable     {@link GoToTable} instruction value
107      * @param priority      of the flow
108      * @param peerEndpoint  original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
109      * @param externalPorts list of external {@link NodeConnectorId}-s get from node
110      * @param ofWriter      flow writer
111      */
112     void createExternalL2Flow(short goToTable, int priority, Endpoint peerEndpoint, Set<NodeConnectorId> externalPorts,
113                               OfWriter ofWriter) {
114         OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals = utils.getEndpointOrdinals(peerEndpoint);
115         if (peerOrdinals != null) {
116             MatchBuilder matchBuilder = new MatchBuilder()
117                     .setEthernetMatch(ethernetMatch(null, peerEndpoint.getMacAddress(), null));
118             addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) peerOrdinals.getBdId()));
119             Match match = matchBuilder.build();
120
121             long port;
122             for (NodeConnectorId externalPort : externalPorts) {
123                 try {
124                     port = getOfPortNum(externalPort);
125                     writeExternalL2Flow(goToTable, priority, peerOrdinals, port, match, ofWriter);
126                 } catch (NumberFormatException e) {
127                     LOG.warn("Invalid NodeConnectorId. External port: {}", externalPort);
128                 }
129             }
130         }
131     }
132
133     /**
134      * Create external L3 flow for every external port found on node
135      *
136      * @param goToTable     {@link GoToTable} instruction value
137      * @param priority      of the flow
138      * @param peerEndpoint  to original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
139      * @param l2GatewayEp   L2 endpoint of subnet gateway
140      * @param destL3Address endpoint L3 address
141      * @param externalPorts list of external {@link NodeConnectorId}-s get from node
142      * @param ofWriter      flow writer
143      */
144     void createExternalL3RoutedFlow(short goToTable, int priority, Endpoint peerEndpoint, Endpoint l2GatewayEp,
145                                     L3Address destL3Address, Set<NodeConnectorId> externalPorts, OfWriter ofWriter) {
146         OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals = utils.getEndpointOrdinals(peerEndpoint);
147         if (peerOrdinals != null) {
148             Layer3Match layer3Match;
149             Long etherType;
150             String ikey;
151             if (destL3Address.getIpAddress() != null && destL3Address.getIpAddress().getIpv4Address() != null) {
152                 ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
153                 etherType = IPv4;
154                 layer3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
155             } else if (destL3Address.getIpAddress() != null && destL3Address.getIpAddress().getIpv6Address() != null) {
156                 ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
157                 etherType = IPv6;
158                 layer3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
159             } else {
160                 LOG.error("Endpoint has Ip Address that is not recognised as either IPv4 or IPv6.", destL3Address);
161                 return;
162             }
163             MacAddress matcherMac = peerEndpoint.getMacAddress();
164             MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
165                     .setLayer3Match(layer3Match);
166             addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) peerOrdinals.getL3Id()));
167             Match match = matchBuilder.build();
168
169             long port;
170             for (NodeConnectorId externalPort : externalPorts) {
171                 try {
172                     port = getOfPortNum(externalPort);
173                     writeExternalL3RoutedFlow(goToTable, priority, port, l2GatewayEp, match, peerOrdinals, ofWriter);
174                 } catch (NumberFormatException e) {
175                     LOG.warn("Invalid NodeConnectorId. External port: {}", externalPort);
176                 }
177             }
178         }
179     }
180
181     /**
182      * Create local L2 flow
183      *
184      * @param goToTable {@link GoToTable} instruction value
185      * @param priority  of the flow
186      * @param endpoint  original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
187      * @param ofWriter  flow writer
188      */
189     void createLocalL2Flow(short goToTable, int priority, Endpoint endpoint, OfWriter ofWriter) {
190         OfOverlayContext context = endpoint.getAugmentation(OfOverlayContext.class);
191         OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
192
193         Action setNextHop;
194         try {
195             setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(getOfPortNum(context.getNodeConnectorId())));
196         } catch (NumberFormatException ex) {
197             LOG.warn("Could not parse port number {}", context.getNodeConnectorId(), ex);
198             return;
199         }
200         List<Action> applyActions = new ArrayList<>();
201         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
202         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
203         applyActions.add(setNextHop);
204
205         int order = 0;
206         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
207                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
208                 .build();
209         Instruction gotoTable = new InstructionBuilder().setOrder(order)
210                 .setInstruction(gotoTableIns(goToTable))
211                 .build();
212
213         ArrayList<Instruction> instructions = new ArrayList<>();
214         instructions.add(applyActionsIns);
215         instructions.add(gotoTable);
216
217         MatchBuilder matchBuilder = new MatchBuilder()
218                 .setEthernetMatch(ethernetMatch(null, endpoint.getMacAddress(), null));
219         addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) ordinals.getBdId()));
220         Match match = matchBuilder.build();
221         FlowId flowId = FlowIdUtils.newFlowId(tableId, "localL2", match);
222         FlowBuilder flowBuilder = base(tableId).setId(flowId)
223                 .setPriority(priority)
224                 .setMatch(match)
225                 .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
226         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
227     }
228
229     /**
230      * Create local L3 routed flow
231      *
232      * @param goToTable   {@link GoToTable} instruction value
233      * @param priority    of the flow
234      * @param endpoint    original endpoint (input parameter to {@link DestinationMapper#sync(Endpoint, OfWriter)}
235      * @param l3Address   endpoint L3 address
236      * @param localSubnet subnet from local node
237      * @param destSubnet  destination endpoint's subnet
238      * @param ofWriter    flow writer
239      */
240     void createLocalL3RoutedFlow(short goToTable, int priority, Endpoint endpoint, L3Address l3Address,
241                                  Subnet localSubnet, Subnet destSubnet, OfWriter ofWriter) {
242         NodeConnectorId connectorId = endpoint.getAugmentation(OfOverlayContext.class).getNodeConnectorId();
243         L3Context l3Context = utils.getL3ContextForSubnet(utils.getIndexedTenant(endpoint.getTenant()), localSubnet);
244         if (l3Context == null) {
245             return;
246         }
247         MacAddress matcherMac = utils.routerPortMac(l3Context, localSubnet.getVirtualRouterIp(), endpoint.getTenant());
248         MacAddress epDestMac = endpoint.getMacAddress();
249         if (matcherMac == null || epDestMac == null) {
250             return;
251         }
252         MacAddress destSubnetGatewayMac = utils.routerPortMac(l3Context, destSubnet.getVirtualRouterIp(),
253                 endpoint.getTenant());
254         OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
255         if (localSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
256             matcherMac = epDestMac;
257         }
258         Action setNextHopAction;
259         try {
260             setNextHopAction = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(getOfPortNum(connectorId)));
261         } catch (NumberFormatException ex) {
262             LOG.warn("Could not parse port number {}", connectorId, ex);
263             return;
264         }
265         List<Action> l3ApplyActions = new ArrayList<>();
266         l3ApplyActions.add(setDlDstAction(epDestMac));
267         l3ApplyActions.add(decNwTtlAction());
268         if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
269             l3ApplyActions.add(setDlSrcAction(destSubnetGatewayMac));
270         }
271         List<Action> applyActions = new ArrayList<>();
272         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
273         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
274         applyActions.add(setNextHopAction);
275         applyActions.addAll(l3ApplyActions);
276
277         int order = 0;
278         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
279                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
280                 .build();
281         Instruction gotoTable = new InstructionBuilder().setOrder(order)
282                 .setInstruction(gotoTableIns(goToTable))
283                 .build();
284         ArrayList<Instruction> l3instructions = new ArrayList<>();
285         l3instructions.add(applyActionsIns);
286         l3instructions.add(gotoTable);
287         Layer3Match l3Match;
288         Long etherType;
289         String ikey;
290         if (l3Address.getIpAddress() != null && l3Address.getIpAddress().getIpv4Address() != null) {
291             ikey = l3Address.getIpAddress().getIpv4Address().getValue() + "/32";
292             etherType = IPv4;
293             l3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
294         } else if (l3Address.getIpAddress() != null && l3Address.getIpAddress().getIpv6Address() != null) {
295             ikey = l3Address.getIpAddress().getIpv6Address().getValue() + "/128";
296             etherType = IPv6;
297             l3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
298         } else {
299             LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", l3Address.toString());
300             return;
301         }
302         MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
303                 .setLayer3Match(l3Match);
304         addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
305         Match match = matchBuilder.build();
306         FlowId flowid = FlowIdUtils.newFlowId(tableId, "localL3", match);
307         FlowBuilder flowBuilder = base(tableId).setId(flowid)
308                 .setPriority(priority)
309                 .setMatch(match)
310                 .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
311         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
312     }
313
314     /**
315      * Create remote L2 flow
316      *
317      * @param goToTable    {@link GoToTable} instruction value
318      * @param priority     of the flow
319      * @param endpoint     original peer
320      * @param peerEndpoint peer endpoint to original endpoint
321      * @param tunDst       tunnel destination Ip address
322      * @param connectorId  tunnel port
323      * @param ofWriter     flow writer
324      */
325     void createRemoteL2Flow(short goToTable, int priority, Endpoint endpoint, Endpoint peerEndpoint, IpAddress tunDst,
326                             NodeConnectorId connectorId, OfWriter ofWriter) {
327         OrdinalFactory.EndpointFwdCtxOrdinals endpointOrdinals = utils.getEndpointOrdinals(endpoint);
328         long port;
329         try {
330             port = getOfPortNum(connectorId);
331         } catch (NumberFormatException ex) {
332             LOG.warn("Could not parse port number {}", connectorId);
333             return;
334         }
335         Action tunnelDestinationAction = null;
336         if (tunDst.getIpv4Address() != null) {
337             tunnelDestinationAction = nxLoadTunIPv4Action(tunDst.getIpv4Address().getValue(), false);
338         } else if (tunDst.getIpv6Address() != null) {
339             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(),
340                     nodeId);
341             return;
342         }
343         List<Action> applyActions = new ArrayList<>();
344         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(endpointOrdinals.getEpgId())));
345         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(endpointOrdinals.getCgId())));
346         applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
347         applyActions.add(tunnelDestinationAction);
348
349         int order = 0;
350         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
351                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
352                 .build();
353         Instruction gotoTable = new InstructionBuilder().setOrder(order)
354                 .setInstruction(gotoTableIns(goToTable))
355                 .build();
356
357         ArrayList<Instruction> instructions = new ArrayList<>();
358         instructions.add(applyActionsIns);
359         instructions.add(gotoTable);
360
361         MatchBuilder matchBuilder = new MatchBuilder()
362                 .setEthernetMatch(ethernetMatch(null, peerEndpoint.getMacAddress(), null));
363         addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg4.class, (long) endpointOrdinals.getBdId()));
364         Match match = matchBuilder.build();
365         FlowId flowid = FlowIdUtils.newFlowId(tableId, "remoteL2", match);
366         FlowBuilder flowBuilder = base(tableId).setId(flowid)
367                 .setPriority(priority)
368                 .setMatch(match)
369                 .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
370
371         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
372     }
373
374     /**
375      * Create remote L3 routed flow
376      *
377      * @param goToTable     {@link GoToTable} instruction value
378      * @param priority      of the flow
379      * @param endpoint      peer
380      * @param destL3Address destination L3 address
381      * @param destSubnet    subnet from destination node
382      * @param tunDst        tunnel destination Ip address
383      * @param connectorId   tunnel port
384      * @param localSubnet   subnet from local node
385      * @param ofWriter      flow writer
386      */
387     void createRemoteL3RoutedFlow(short goToTable, int priority, Endpoint endpoint, L3Address destL3Address,
388                                   Subnet destSubnet, IpAddress tunDst, NodeConnectorId connectorId, Subnet localSubnet,
389                                   OfWriter ofWriter) {
390         L3Context context = utils.getL3ContextForSubnet(utils.getIndexedTenant(endpoint.getTenant()), destSubnet);
391         if (context == null) {
392             return;
393         }
394         OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
395         MacAddress matcherMac = utils.routerPortMac(context, localSubnet.getVirtualRouterIp(), endpoint.getTenant());
396         MacAddress epDestMac = endpoint.getMacAddress();
397         if (matcherMac == null || epDestMac == null) {
398             return;
399         }
400         MacAddress destSubnetGatewayMac = utils.routerPortMac(context, destSubnet.getVirtualRouterIp(),
401                 endpoint.getTenant());
402
403         // L3 Actions
404         List<Action> l3ApplyActions = new ArrayList<>();
405         if (localSubnet.getId().getValue().equals(destSubnet.getId().getValue())) {
406             // This is our final destination, so match on actual EP mac.
407             matcherMac = epDestMac;
408         }
409         if (!(matcherMac.getValue().equals(epDestMac.getValue()))) {
410             Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
411             l3ApplyActions.add(setDlSrc);
412         }
413         l3ApplyActions.add(setDlDstAction(epDestMac));
414         l3ApplyActions.add(decNwTtlAction());
415
416
417         // Actions
418         Action tunnelDestinationAction;
419         if (tunDst != null && tunDst.getIpv4Address() != null) {
420             tunnelDestinationAction = nxLoadTunIPv4Action(tunDst.getIpv4Address().getValue(), false);
421         } else if (tunDst != null && tunDst.getIpv6Address() != null) {
422             LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(), nodeId);
423             return;
424         } else {
425             LOG.error("Tunnel IP for {} invalid", nodeId);
426             return;
427         }
428         long port;
429         try {
430             port = getOfPortNum(connectorId);
431         } catch (NumberFormatException ex) {
432             LOG.warn("Could not parse port number {}", connectorId, ex);
433             return;
434         }
435         List<Action> applyActions = new ArrayList<>();
436         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
437         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
438         applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
439         applyActions.add(tunnelDestinationAction);
440         applyActions.addAll(l3ApplyActions);
441
442         int order = 0;
443         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
444                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
445                 .build();
446         Instruction gotoTable = new InstructionBuilder().setOrder(order)
447                 .setInstruction(gotoTableIns(goToTable))
448                 .build();
449
450         ArrayList<Instruction> l3instructions = new ArrayList<>();
451         l3instructions.add(applyActionsIns);
452         l3instructions.add(gotoTable);
453
454         Layer3Match layer3Match;
455         Long etherType;
456         String ikey;
457         if (destL3Address.getIpAddress().getIpv4Address() != null) {
458             ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
459             etherType = IPv4;
460             layer3Match = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
461         } else {
462             ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
463             etherType = IPv6;
464             layer3Match = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
465         }
466         MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
467                 .setLayer3Match(layer3Match);
468         addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
469         Match match = matchBuilder.build();
470         FlowId flowid = FlowIdUtils.newFlowId(tableId, "remoteL3", match);
471         FlowBuilder flowBuilder = base(tableId).setId(flowid)
472                 .setPriority(priority)
473                 .setMatch(match)
474                 .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
475         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
476     }
477
478     /**
479      * Creates arp flow using virtual router IP in {@link Subnet}
480      *
481      * @param priority      of the flow
482      * @param indexedTenant of the {@link Endpoint}
483      * @param subnet        entries get from peer's tenants
484      * @param ofWriter      flow writer
485      * @throws Exception could be thrown during {@link OrdinalFactory#getContextOrdinal(TenantId, UniqueId)}. Handled
486      *                   in {@link DestinationMapper#syncArpFlow(DestinationMapperFlows, TenantId, OfWriter)}
487      */
488     void createRouterArpFlow(int priority, IndexedTenant indexedTenant, Subnet subnet, OfWriter ofWriter)
489             throws Exception {
490         Tenant tenant = indexedTenant.getTenant();
491         if (tenant != null) {
492             L3Context l3Context = utils.getL3ContextForSubnet(indexedTenant, subnet);
493             if (l3Context != null) {
494                 int contextOrdinal = OrdinalFactory.getContextOrdinal(tenant.getId(), l3Context.getId());
495                 MacAddress routerMac = utils.routerPortMac(l3Context, subnet.getVirtualRouterIp(),
496                         indexedTenant.getTenant().getId());
497                 if (routerMac != null) {
498                     if (subnet.getVirtualRouterIp().getIpv4Address() == null
499                             && subnet.getVirtualRouterIp().getIpv6Address() != null) {
500                         LOG.warn("IPv6 virtual router {} for subnet {} not supported", subnet.getVirtualRouterIp(), subnet.getId()
501                                 .getValue());
502                         return;
503                     }
504                     String ipv4Value = subnet.getVirtualRouterIp().getIpv4Address().getValue();
505                     BigInteger intRouterMac = new BigInteger(1, bytesFromHexString(routerMac.getValue()));
506                     MatchBuilder matchBuilder = new MatchBuilder()
507                             .setEthernetMatch(ethernetMatch(null, null, ARP))
508                             .setLayer3Match(new ArpMatchBuilder()
509                                     .setArpOp(1)
510                                     .setArpTargetTransportAddress(new Ipv4Prefix(ipv4Value + "/32"))
511                                     .build());
512                     addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) contextOrdinal));
513                     Match match = matchBuilder.build();
514                     FlowId flowId = FlowIdUtils.newFlowId(tableId, "routerarp", match);
515                     FlowBuilder flowBuilder = base(tableId).setPriority(priority)
516                             .setId(flowId)
517                             .setMatch(match)
518                             .setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(),
519                                     setDlSrcAction(routerMac), nxLoadArpOpAction(BigInteger.valueOf(2L)),
520                                     nxMoveArpShaToArpThaAction(), nxLoadArpShaAction(intRouterMac),
521                                     nxMoveArpSpaToArpTpaAction(), nxLoadArpSpaAction(ipv4Value),
522                                     outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT")))));
523                     ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
524                 }
525             } else {
526                 LOG.error("No L3 Context found associated with subnet {}.", subnet.getId());
527             }
528         }
529     }
530
531     /**
532      * Broadcast flow for destination mapper
533      *
534      * @param priority of the flow
535      * @param ordinals of the endpoint (input parameter in {@link DestinationMapper#sync(Endpoint, OfWriter)})
536      * @param mac      address of the multicast router {@link DestinationMapper#MULTICAST_MAC}
537      * @param ofWriter flow writer
538      */
539     void createBroadcastFlow(int priority, OrdinalFactory.EndpointFwdCtxOrdinals ordinals, MacAddress mac,
540                              OfWriter ofWriter) {
541         MatchBuilder matchBuilder = new MatchBuilder()
542                 .setEthernetMatch(new EthernetMatchBuilder()
543                         .setEthernetDestination(new EthernetDestinationBuilder().setAddress(mac).setMask(mac).build())
544                         .build());
545         addNxRegMatch(matchBuilder, FlowUtils.RegMatch.of(NxmNxReg5.class, (long) ordinals.getFdId()));
546         Match match = matchBuilder.build();
547         FlowId flowId = FlowIdUtils.newFlowId(tableId, "broadcast", match);
548         FlowBuilder flowBuilder = base(tableId)
549                 .setPriority(priority)
550                 .setId(flowId)
551                 .setMatch(match)
552                 .setInstructions(instructions(applyActionIns(nxLoadTunIdAction(BigInteger
553                         .valueOf(ordinals.getFdId()), false), groupAction((long) ordinals.getFdId()))));
554         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
555     }
556
557     /**
558      * L3 prefix flow is created with endpoint {@link NodeConnectorId} if internal. If endpoint is external and
559      * external ports are present, one flow per external port is created
560      *
561      * @param goToTable     policy enforcer table Id
562      * @param priority      of the flow
563      * @param gatewayEp      L2 endpoint, should contain {@link MacAddress} and {@link OrdinalFactory.EndpointFwdCtxOrdinals}
564      * @param l3Prefix      endpoint L3 prefix value
565      * @param tenant        value get from {@link L3Prefix}
566      * @param localSubnet   value where this node is present
567      * @param externalPorts list of all external ports
568      * @param ofWriter      flow writer
569      */
570     void createL3PrefixFlow(short goToTable, int priority, Endpoint gatewayEp, EndpointL3Prefix l3Prefix, IndexedTenant tenant,
571                             Subnet localSubnet, Set<NodeConnectorId> externalPorts, OfWriter ofWriter) {
572         L3Context l3Context = utils.getL3ContextForSubnet(tenant, localSubnet);
573         if (l3Context != null && localSubnet.getVirtualRouterIp() != null) {
574             MacAddress matcherMacAddress = utils.routerPortMac(l3Context, localSubnet.getVirtualRouterIp(),
575                     tenant.getTenant().getId());
576             OfOverlayContext context = gatewayEp.getAugmentation(OfOverlayContext.class);
577             if (EndpointManager.isInternal(gatewayEp, tenant.getExternalImplicitGroups())) {
578                 Preconditions.checkNotNull(context.getNodeConnectorId());
579                 try {
580                     Long port = getOfPortNum(context.getNodeConnectorId());
581                     if(matcherMacAddress != null) {
582                         writeL3PrefixFlow(priority, goToTable, gatewayEp, l3Prefix, port, matcherMacAddress, ofWriter);
583                     }
584                 } catch (NumberFormatException e) {
585                     LOG.warn("Could not parse port number {}", context.getNodeConnectorId());
586                 }
587             } else { // External
588                 for (NodeConnectorId externalPort : externalPorts) {
589                     try {
590                         Long port = getOfPortNum(externalPort);
591                         if(matcherMacAddress != null) {
592                             writeL3PrefixFlow(priority, goToTable, gatewayEp, l3Prefix, port, matcherMacAddress, ofWriter);
593                         }
594                     } catch (NumberFormatException e) {
595                         LOG.warn("Could not parse port number {}", externalPort);
596                     }
597                 }
598             }
599         }
600     }
601
602     private void writeExternalL2Flow(short goToTable, int priority, OrdinalFactory.EndpointFwdCtxOrdinals ordinals,
603                                      Long port, Match match, OfWriter ofWriter) {
604         List<Action> applyActions = new ArrayList<>();
605         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId())));
606         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId())));
607         applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
608
609         int order = 0;
610         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
611                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
612                 .build();
613         Instruction gotoTable = new InstructionBuilder().setOrder(order)
614                 .setInstruction(gotoTableIns(goToTable))
615                 .build();
616
617         ArrayList<Instruction> instructions = new ArrayList<>();
618         instructions.add(applyActionsIns);
619         instructions.add(gotoTable);
620
621         FlowId flowId = FlowIdUtils.newFlowId(tableId, "externalL2", match);
622         FlowBuilder flowBuilder = base(tableId).setId(flowId)
623                 .setPriority(priority)
624                 .setMatch(match)
625                 .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
626         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
627     }
628
629     private void writeExternalL3RoutedFlow(short goToTable, int priority, long port, Endpoint l2GatewayEp, Match match,
630                                            OrdinalFactory.EndpointFwdCtxOrdinals peerOrdinals, OfWriter ofWriter) {
631         MacAddress destSubnetGatewayMac = l2GatewayEp.getMacAddress();
632
633         List<Action> l3ApplyActions = new ArrayList<>();
634         l3ApplyActions.add(setDlSrcAction(destSubnetGatewayMac));
635         l3ApplyActions.add(setDlDstAction(l2GatewayEp.getMacAddress()));
636
637         List<Action> applyActions = new ArrayList<>();
638         applyActions.add(nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(peerOrdinals.getEpgId())));
639         applyActions.add(nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(peerOrdinals.getCgId())));
640         applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
641         applyActions.addAll(l3ApplyActions);
642
643         int order = 0;
644         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
645                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
646                 .build();
647         Instruction gotoTable = new InstructionBuilder().setOrder(order)
648                 .setInstruction(gotoTableIns(goToTable))
649                 .build();
650         ArrayList<Instruction> l3instructions = new ArrayList<>();
651         l3instructions.add(applyActionsIns);
652         l3instructions.add(gotoTable);
653
654         FlowId flowid = FlowIdUtils.newFlowId(tableId, "externalL3", match);
655         FlowBuilder flowBuilder = base(tableId).setId(flowid)
656                 .setPriority(priority)
657                 .setMatch(match)
658                 .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
659         ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
660     }
661
662     private void writeL3PrefixFlow(int priority, short goToTable, Endpoint endpoint, EndpointL3Prefix l3Prefix,
663                                    Long port, MacAddress matcherMacAddress, OfWriter ofWriter) {
664         MacAddress macAddress = endpoint.getMacAddress();
665         OrdinalFactory.EndpointFwdCtxOrdinals ordinals = utils.getEndpointOrdinals(endpoint);
666         Action setEpgAction = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(ordinals.getEpgId()));
667         Action setCgAction = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(ordinals.getCgId()));
668
669         List<Action> l3ApplyActions = new ArrayList<>();
670         l3ApplyActions.add(setDlDstAction(macAddress));
671         l3ApplyActions.add(decNwTtlAction());
672         List<Action> applyActions = new ArrayList<>();
673         applyActions.add(setEpgAction);
674         applyActions.add(setCgAction);
675         applyActions.add(nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(port)));
676         applyActions.addAll(l3ApplyActions);
677
678         int order = 0;
679         ArrayList<Instruction> l3instructions = new ArrayList<>();
680         Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
681                 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
682                 .build();
683         Instruction gotoTable = new InstructionBuilder().setOrder(order)
684                 .setInstruction(gotoTableIns(goToTable))
685                 .build();
686         l3instructions.add(applyActionsIns);
687         l3instructions.add(gotoTable);
688
689         if(l3Prefix.getIpPrefix() != null) {
690             Long etherType;
691             Integer prefixLength;
692             if (l3Prefix.getIpPrefix().getIpv4Prefix() != null) {
693                 etherType = IPv4;
694                 prefixLength = Integer.valueOf(l3Prefix.getIpPrefix().getIpv4Prefix().getValue().split("/")[1]);
695             } else if (l3Prefix.getIpPrefix().getIpv6Prefix() != null) {
696                 etherType = IPv6;
697                 prefixLength = Integer.valueOf(l3Prefix.getIpPrefix().getIpv6Prefix().getValue().split("/")[1]);
698             } else {
699                 LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", l3Prefix);
700                 return;
701             }
702             MatchBuilder matchBuilder = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMacAddress, etherType));
703             addNxRegMatch(matchBuilder, RegMatch.of(NxmNxReg6.class, (long) ordinals.getL3Id()));
704             Match match = matchBuilder.build();
705
706             FlowId flowid = FlowIdUtils.newFlowId(tableId, "L3prefix", match);
707             FlowBuilder flowBuilder = base(tableId).setId(flowid)
708                     .setPriority(priority + prefixLength)
709                     .setMatch(match)
710                     .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
711             ofWriter.writeFlow(nodeId, tableId, flowBuilder.build());
712         }
713     }
714 }