Freeze upstream versions
[netvirt.git] / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / utils / Ipv6ServiceUtils.java
1 /*
2  * Copyright © 2016, 2017 Red Hat, 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.netvirt.ipv6service.utils;
10
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.concurrent.ExecutionException;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.apache.commons.lang3.StringUtils;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.genius.ipv6util.api.Icmpv6Type;
23 import org.opendaylight.genius.ipv6util.api.Ipv6Constants;
24 import org.opendaylight.genius.ipv6util.api.Ipv6Util;
25 import org.opendaylight.genius.mdsalutil.ActionInfo;
26 import org.opendaylight.genius.mdsalutil.FlowEntity;
27 import org.opendaylight.genius.mdsalutil.InstructionInfo;
28 import org.opendaylight.genius.mdsalutil.MDSALUtil;
29 import org.opendaylight.genius.mdsalutil.MatchInfo;
30 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.NwConstants.NxmOfFieldType;
33 import org.opendaylight.genius.mdsalutil.actions.ActionLearn;
34 import org.opendaylight.genius.mdsalutil.actions.ActionLearn.FlowMod;
35 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationEth;
36 import org.opendaylight.genius.mdsalutil.actions.ActionMoveSourceDestinationIpv6;
37 import org.opendaylight.genius.mdsalutil.actions.ActionNdOptionType;
38 import org.opendaylight.genius.mdsalutil.actions.ActionNdReserved;
39 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
40 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
41 import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
42 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
43 import org.opendaylight.genius.mdsalutil.actions.ActionSetIcmpv6Type;
44 import org.opendaylight.genius.mdsalutil.actions.ActionSetIpv6NdTarget;
45 import org.opendaylight.genius.mdsalutil.actions.ActionSetIpv6NdTll;
46 import org.opendaylight.genius.mdsalutil.actions.ActionSetSourceIpv6;
47 import org.opendaylight.genius.mdsalutil.ericmatches.MatchNdOptionType;
48 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
49 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
50 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
51 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
52 import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
53 import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
54 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6NdTarget;
55 import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
56 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
57 import org.opendaylight.genius.mdsalutil.packet.IPProtocols;
58 import org.opendaylight.genius.utils.ServiceIndex;
59 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
60 import org.opendaylight.mdsal.binding.api.DataBroker;
61 import org.opendaylight.mdsal.binding.api.ReadTransaction;
62 import org.opendaylight.mdsal.binding.util.Datastore;
63 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
64 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
65 import org.opendaylight.mdsal.binding.util.TypedReadWriteTransaction;
66 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
67 import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
68 import org.opendaylight.netvirt.ipv6service.VirtualSubnet;
69 import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
76 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceBindings;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceModeIngress;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.ServiceTypeFlowBased;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.StypeOpenflowBuilder;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfo;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.ServicesInfoKey;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.config.rev181010.Ipv6serviceConfig;
89 import org.opendaylight.yangtools.yang.binding.DataObject;
90 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
91 import org.opendaylight.yangtools.yang.common.Uint64;
92 import org.slf4j.Logger;
93 import org.slf4j.LoggerFactory;
94
95 @Singleton
96 public class Ipv6ServiceUtils {
97     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ServiceUtils.class);
98     public static final Ipv6Address ALL_NODES_MCAST_ADDR = newIpv6Address(Ipv6Constants.ALL_NODES_MCAST_ADDRESS);
99     public static final Ipv6Address UNSPECIFIED_ADDR = newIpv6Address("0:0:0:0:0:0:0:0");
100
101     @Nullable
102     private static Ipv6Address newIpv6Address(String ip) {
103         try {
104             return Ipv6Address.getDefaultInstance(InetAddress.getByName(ip).getHostAddress());
105         } catch (UnknownHostException e) {
106             LOG.error("Ipv6ServiceUtils: Error instantiating ipv6 address", e);
107             return null;
108         }
109     }
110
111     private final DataBroker broker;
112     private final IMdsalApiManager mdsalUtil;
113     private final IpV6NAConfigHelper ipV6NAConfigHelper;
114     private final ManagedNewTransactionRunner txRunner;
115     private final Ipv6serviceConfig ipv6serviceConfig;
116
117     @Inject
118     public Ipv6ServiceUtils(DataBroker broker, IMdsalApiManager mdsalUtil,IpV6NAConfigHelper ipV6NAConfigHelper,
119                             Ipv6serviceConfig ipv6ServiceConfig) {
120         this.broker = broker;
121         this.mdsalUtil = mdsalUtil;
122         this.ipV6NAConfigHelper = ipV6NAConfigHelper;
123         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
124         this.ipv6serviceConfig = ipv6ServiceConfig;
125     }
126
127     /**
128      * Retrieves the object from the datastore.
129      * @param datastoreType the data store type.
130      * @param path the wild card path.
131      * @return the required object.
132      */
133     public <T extends DataObject> Optional<T> read(LogicalDatastoreType datastoreType, InstanceIdentifier<T> path) {
134         try (ReadTransaction tx = broker.newReadOnlyTransaction()) {
135             return tx.read(datastoreType, path).get();
136         } catch (InterruptedException | ExecutionException e) {
137             throw new RuntimeException(e);
138         }
139     }
140
141     /**
142      * Retrieves the Interface from the datastore.
143      * @param interfaceName the interface name
144      * @return the interface.
145      */
146     public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
147         .@Nullable Interface getInterface(String interfaceName) {
148         return read(LogicalDatastoreType.CONFIGURATION, getInterfaceIdentifier(interfaceName)).orElse(null);
149     }
150
151     /**
152      * Builds the interface identifier.
153      * @param interfaceName the interface name.
154      * @return the interface identifier.
155      */
156     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
157             .interfaces.Interface> getInterfaceIdentifier(String interfaceName) {
158         return InstanceIdentifier.builder(Interfaces.class)
159                 .child(
160                         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
161                                 .Interface.class, new InterfaceKey(interfaceName)).build();
162     }
163
164     /**
165      * Build the interface state.
166      * @param interfaceName the interface name.
167      * @return the interface state.
168      */
169     public static InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508
170             .interfaces.state.Interface> buildStateInterfaceId(String interfaceName) {
171         InstanceIdentifier.InstanceIdentifierBuilder<Interface> idBuilder =
172                 InstanceIdentifier.builder(InterfacesState.class)
173                 .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces
174                         .state.Interface.class,
175                         new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces
176                         .rev140508.interfaces.state.InterfaceKey(interfaceName));
177         return idBuilder.build();
178     }
179
180     /**
181      * Retrieves the interface state.
182      * @param interfaceName the interface name.
183      * @return the interface state.
184      */
185     public org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state
186             .@Nullable Interface getInterfaceStateFromOperDS(String interfaceName) {
187         return MDSALUtil.read(LogicalDatastoreType.OPERATIONAL, buildStateInterfaceId(interfaceName), broker)
188                 .orElse(null);
189     }
190
191     private static List<MatchInfo> getIcmpv6RSMatch(Long elanTag) {
192         List<MatchInfo> matches = new ArrayList<>();
193         matches.add(MatchEthernetType.IPV6);
194         matches.add(MatchIpProtocol.ICMPV6);
195         matches.add(new MatchIcmpv6(Icmpv6Type.ROUTER_SOLICITATION.getValue(), (short) 0));
196         matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
197         return matches;
198     }
199
200     private List<MatchInfo> getIcmpv6NSMatch(Long elanTag, Ipv6Address ipv6Address) {
201         List<MatchInfo> matches = new ArrayList<>();
202         matches.add(MatchEthernetType.IPV6);
203         matches.add(MatchIpProtocol.ICMPV6);
204         matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_SOLICITATION.getValue(), (short) 0));
205         matches.add(new MatchIpv6NdTarget(ipv6Address));
206         matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
207         return matches;
208     }
209
210     private List<MatchInfo> getIcmpv6NAMatch(Long elanTag) {
211         List<MatchInfo> matches = new ArrayList<>();
212         matches.add(MatchEthernetType.IPV6);
213         matches.add(MatchIpProtocol.ICMPV6);
214         matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_ADVERTISEMENT.getValue(), (short) 0));
215         matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
216         return matches;
217     }
218
219     private static String getIPv6FlowRef(Uint64 dpId, Long elanTag, String flowType) {
220         return new StringBuilder().append(Ipv6ServiceConstants.FLOWID_PREFIX)
221                 .append(dpId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
222                 .append(elanTag).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
223                 .append(flowType).toString();
224     }
225
226     public void installIcmpv6NsPuntFlow(short tableId, Uint64 dpId, Long elanTag, Ipv6Address ipv6Address,
227             int addOrRemove) {
228         String flowId = getIPv6FlowRef(dpId, elanTag, Ipv6Util.getFormattedIpv6Address(ipv6Address));
229
230         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
231             LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
232             LoggingFutures
233                     .addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
234                         mdsalUtil.removeFlow(tx, dpId, flowId, tableId);
235                     }), LOG, "Error while removing flow={}", flowId);
236         } else {
237             List<ActionInfo> actionsInfos = new ArrayList<>();
238             actionsInfos.add(new ActionPuntToController());
239
240             int ndPuntTimeout = ipv6serviceConfig.getNeighborDiscoveryPuntTimeout().toJava();
241             if (isNdPuntProtectionEnabled(ndPuntTimeout)) {
242                 actionsInfos.add(getLearnActionForNsPuntProtection(ndPuntTimeout));
243             }
244             List<InstructionInfo> instructions = Arrays.asList(new InstructionApplyActions(actionsInfos));
245             List<MatchInfo> nsMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
246             FlowEntity nsFlowEntity =
247                     MDSALUtil.buildFlowEntity(dpId, tableId, flowId, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
248                             "IPv6NS", 0, 0, NwConstants.COOKIE_IPV6_TABLE, nsMatch, instructions);
249
250             LOG.trace("Installing IPv6 Neighbor Solicitation Flow DpId={}, elanTag={} ipv6Address={}", dpId, elanTag,
251                     ipv6Address.getValue());
252             LoggingFutures
253                     .addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
254                         mdsalUtil.addFlow(tx, nsFlowEntity);
255                     }), LOG, "Error while adding flow={}", nsFlowEntity);
256         }
257     }
258
259     private static String getIPv6OvsFlowRef(short tableId, Uint64 dpId, int lportTag, String ndTargetAddr) {
260         return new StringBuilder().append(Ipv6ServiceConstants.FLOWID_PREFIX)
261                 .append(dpId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
262                 .append(tableId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
263                 .append(lportTag).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
264                 .append(ndTargetAddr).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
265                 .append(Ipv6ServiceConstants.FLOWID_NS_RESPONDER_SUFFIX).toString();
266     }
267
268     private static String getIPv6OvsFlowRef(short tableId, Uint64 dpId, int lportTag, String ndTargetAddr,
269                                             String vmMacAddress) {
270         return new StringBuilder().append(Ipv6ServiceConstants.FLOWID_PREFIX)
271                 .append(dpId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
272                 .append(tableId).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
273                 .append(lportTag).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
274                 .append(vmMacAddress).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
275                 .append(ndTargetAddr).append(Ipv6ServiceConstants.FLOWID_SEPARATOR)
276                 .append(Ipv6ServiceConstants.FLOWID_NS_RESPONDER_SUFFIX).toString();
277     }
278
279     private ActionLearn getLearnActionForNsPuntProtection(int ndPuntTimeout) {
280         List<FlowMod> flowMods = getFlowModsForIpv6PuntProtection(Icmpv6Type.NEIGHBOR_SOLICITATION);
281         flowMods.add(new ActionLearn.MatchFromField(NxmOfFieldType.NXM_NX_ND_TARGET.getType(),
282                 NxmOfFieldType.NXM_NX_ND_TARGET.getType(), NxmOfFieldType.NXM_NX_ND_TARGET.getFlowModHeaderLenInt()));
283
284         return new ActionLearn(0, ndPuntTimeout, Ipv6ServiceConstants.NS_PUNT_PROTECTION_FLOW_PRIORITY,
285                 NwConstants.COOKIE_IPV6_TABLE, 0, NwConstants.IPV6_TABLE, 0, 0, flowMods);
286     }
287
288     public void installIcmpv6RsPuntFlow(short tableId, Uint64 dpId, Long elanTag, int addOrRemove) {
289         if (dpId == null || dpId.equals(Ipv6ServiceConstants.INVALID_DPID)) {
290             return;
291         }
292         String flowId = getIPv6FlowRef(dpId, elanTag, "IPv6RS");
293         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
294             LOG.trace("Removing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
295             LoggingFutures
296                     .addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
297                         mdsalUtil.removeFlow(tx, dpId, flowId, tableId);
298                     }), LOG, "Error while removing flow={}", flowId);
299         } else {
300             List<ActionInfo> actionsInfos = new ArrayList<>();
301             // Punt to controller
302             actionsInfos.add(new ActionPuntToController());
303
304             int rdPuntTimeout = ipv6serviceConfig.getRouterDiscoveryPuntTimeout().toJava();
305             if (isRdPuntProtectionEnabled(rdPuntTimeout)) {
306                 actionsInfos.add(getLearnActionForRsPuntProtection(rdPuntTimeout));
307             }
308             List<InstructionInfo> instructions = Arrays.asList(new InstructionApplyActions(actionsInfos));
309             List<MatchInfo> routerSolicitationMatch = getIcmpv6RSMatch(elanTag);
310             FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
311                     flowId,Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY, "IPv6RS", 0, 0,
312                     NwConstants.COOKIE_IPV6_TABLE, routerSolicitationMatch, instructions);
313
314             LOG.trace("Installing IPv6 Router Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
315             LoggingFutures
316                     .addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION, tx -> {
317                         mdsalUtil.addFlow(tx, rsFlowEntity);
318                     }), LOG, "Error while adding flow={}", rsFlowEntity);
319         }
320     }
321
322     private ActionLearn getLearnActionForRsPuntProtection(int rdPuntTimeout) {
323         return new ActionLearn(0, rdPuntTimeout, Ipv6ServiceConstants.RS_PUNT_PROTECTION_FLOW_PRIORITY,
324                 NwConstants.COOKIE_IPV6_TABLE, 0, NwConstants.IPV6_TABLE, 0, 0,
325                 getFlowModsForIpv6PuntProtection(Icmpv6Type.ROUTER_SOLICITATION));
326     }
327
328     private List<FlowMod> getFlowModsForIpv6PuntProtection(Icmpv6Type icmpv6Type) {
329         return new ArrayList<>(Arrays.asList(
330                 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6, NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
331                         NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
332                 new ActionLearn.MatchFromValue(IPProtocols.IPV6ICMP.shortValue(),
333                         NxmOfFieldType.NXM_OF_IP_PROTO.getType(),
334                         NxmOfFieldType.NXM_OF_IP_PROTO.getFlowModHeaderLenInt()),
335                 new ActionLearn.MatchFromValue(icmpv6Type.getValue(), NxmOfFieldType.NXM_OF_ICMPv6_TYPE.getType(),
336                         NxmOfFieldType.NXM_OF_ICMPv6_TYPE.getFlowModHeaderLenInt()),
337                 new ActionLearn.MatchFromField(NxmOfFieldType.NXM_OF_ICMPv6_CODE.getType(),
338                         NxmOfFieldType.NXM_OF_ICMPv6_CODE.getType(),
339                         NxmOfFieldType.NXM_OF_ICMPv6_CODE.getFlowModHeaderLenInt()),
340                 new ActionLearn.MatchFromField(NxmOfFieldType.OXM_OF_METADATA.getType(),
341                         MetaDataUtil.METADATA_LPORT_TAG_OFFSET, NxmOfFieldType.OXM_OF_METADATA.getType(),
342                         MetaDataUtil.METADATA_LPORT_TAG_OFFSET, MetaDataUtil.METADATA_LPORT_TAG_BITLEN)));
343     }
344
345     private boolean isRdPuntProtectionEnabled(int rdPuntTimeout) {
346         return rdPuntTimeout != 0;
347     }
348
349     private boolean isNdPuntProtectionEnabled(int ndPuntTimeout) {
350         return ndPuntTimeout != 0;
351     }
352
353     public void installIcmpv6NaForwardFlow(short tableId, IVirtualPort vmPort, Uint64 dpId, Long elanTag,
354             int addOrRemove) {
355         List<MatchInfo> matches = getIcmpv6NAMatch(elanTag);
356         List<InstructionInfo> instructions = new ArrayList<>();
357         List<ActionInfo> actionsInfos = new ArrayList<>();
358         actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
359         instructions.add(new InstructionApplyActions(actionsInfos));
360
361         for (Ipv6Address ipv6Address : vmPort.getIpv6Addresses()) {
362             matches.add(new MatchIpv6Source(ipv6Address.getValue() + NwConstants.IPV6PREFIX));
363             String flowId = getIPv6FlowRef(dpId, elanTag,
364                     vmPort.getIntfUUID().getValue() + Ipv6ServiceConstants.FLOWID_SEPARATOR + ipv6Address.getValue());
365             FlowEntity rsFlowEntity =
366                     MDSALUtil.buildFlowEntity(dpId, tableId, flowId, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
367                             "IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE, matches, instructions);
368             if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
369                 LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
370                         elanTag, ipv6Address.getValue());
371                 mdsalUtil.removeFlow(rsFlowEntity);
372             } else {
373                 LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
374                         elanTag, ipv6Address.getValue());
375                 mdsalUtil.installFlow(rsFlowEntity);
376             }
377         }
378     }
379
380     public void installIcmpv6NaPuntFlow(short tableId, Ipv6Prefix ipv6Prefix, Uint64 dpId, Long elanTag,
381             int addOrRemove) {
382         List<MatchInfo> naMatch = getIcmpv6NAMatch(elanTag);
383         naMatch.add(new MatchIpv6Source(ipv6Prefix));
384
385         List<InstructionInfo> instructions = new ArrayList<>();
386         List<ActionInfo> actionsInfos = new ArrayList<>();
387         actionsInfos.add(new ActionPuntToController());
388         actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
389         instructions.add(new InstructionApplyActions(actionsInfos));
390
391         String flowId = getIPv6FlowRef(dpId, elanTag, "IPv6NA." + ipv6Prefix.getValue());
392         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
393                 flowId, Ipv6ServiceConstants.PUNT_NA_FLOW_PRIORITY,
394                 "IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE, naMatch, instructions);
395         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
396             LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
397             mdsalUtil.removeFlow(rsFlowEntity);
398         } else {
399             LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
400             mdsalUtil.installFlow(rsFlowEntity);
401         }
402     }
403
404     public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
405                                           Uint64 cookie, List<Instruction> instructions) {
406         StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
407                 .setFlowPriority(flowPriority).setInstruction(instructions);
408         return new BoundServicesBuilder().withKey(new BoundServicesKey(servicePriority))
409                 .setServiceName(serviceName).setServicePriority(servicePriority)
410                 .setServiceType(ServiceTypeFlowBased.class)
411                 .addAugmentation(augBuilder.build()).build();
412     }
413
414     private InstanceIdentifier<BoundServices> buildServiceId(String interfaceName,
415                                               short priority) {
416         return InstanceIdentifier.builder(ServiceBindings.class).child(ServicesInfo.class,
417                 new ServicesInfoKey(interfaceName, ServiceModeIngress.class))
418                 .child(BoundServices.class, new BoundServicesKey(priority)).build();
419     }
420
421     public void bindIpv6Service(String interfaceName, Long elanTag, short tableId) {
422         int instructionKey = 0;
423         List<Instruction> instructions = new ArrayList<>();
424         instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(MetaDataUtil.getElanTagMetadata(elanTag),
425                 MetaDataUtil.METADATA_MASK_SERVICE, ++instructionKey));
426         instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(tableId, ++instructionKey));
427         short serviceIndex = ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME, NwConstants.IPV6_SERVICE_INDEX);
428         BoundServices
429                 serviceInfo =
430                 getBoundServices(String.format("%s.%s", "ipv6", interfaceName),
431                         serviceIndex, Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY,
432                         NwConstants.COOKIE_IPV6_TABLE, instructions);
433         MDSALUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
434                 buildServiceId(interfaceName, serviceIndex), serviceInfo);
435     }
436
437     public void unbindIpv6Service(String interfaceName) {
438         MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
439                 buildServiceId(interfaceName, ServiceIndex.getIndex(NwConstants.IPV6_SERVICE_NAME,
440                         NwConstants.IPV6_SERVICE_INDEX)));
441     }
442
443     @Nullable
444     public Uint64 getDpIdFromInterfaceState(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
445             .interfaces.rev140508.interfaces.state.Interface interfaceState) {
446         Uint64 dpId = null;
447         List<String> ofportIds = interfaceState.getLowerLayerIf();
448         if (ofportIds != null && !ofportIds.isEmpty()) {
449             NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
450             dpId = getDpnIdFromNodeConnectorId(nodeConnectorId);
451         }
452         return dpId;
453     }
454
455     public static Uint64 getDpnIdFromNodeConnectorId(NodeConnectorId nodeConnectorId) {
456         Long dpIdLong = MDSALUtil.getDpnIdFromPortName(nodeConnectorId);
457         return dpIdLong < 0 ? Uint64.ZERO : Uint64.valueOf(dpIdLong);
458     }
459
460     public static long getRemoteBCGroup(long elanTag) {
461         return Ipv6ServiceConstants.ELAN_GID_MIN + elanTag % Ipv6ServiceConstants.ELAN_GID_MIN * 2;
462     }
463
464     public static String buildIpv6MonitorJobKey(String ip) {
465         return  "IPv6-"  + ip;
466     }
467
468     public static boolean isVmPort(String deviceOwner) {
469         // FIXME: Currently for VM ports, Neutron is sending deviceOwner as empty instead of "compute:nova".
470         // return Ipv6ServiceConstants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner);
471         return Ipv6ServiceConstants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner)
472                 || StringUtils.isEmpty(deviceOwner);
473     }
474
475     public static boolean isIpv6Subnet(VirtualSubnet subnet) {
476         if (subnet == null) {
477             return false;
478         }
479         return subnet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6) ? true : false;
480     }
481
482     public ActionInfo getLearnActionForNsDrop(Long hardTimeoutinMs) {
483         int hardTimeout = (int)(hardTimeoutinMs / 1000);
484         hardTimeout = hardTimeout > 0 ? hardTimeout : 30;
485         List<ActionLearn.FlowMod> flowMods = Arrays.asList(
486                 new ActionLearn.MatchFromValue(NwConstants.ETHTYPE_IPV6,
487                         NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getType(),
488                         NwConstants.NxmOfFieldType.NXM_OF_ETH_TYPE.getFlowModHeaderLenInt()),
489                 new ActionLearn.MatchFromValue(IPProtocols.IPV6ICMP.intValue(),
490                         NwConstants.NxmOfFieldType.NXM_OF_IP_PROTO.getType(),
491                         NwConstants.NxmOfFieldType.NXM_OF_IP_PROTO.getFlowModHeaderLenInt()),
492                 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
493                         MetaDataUtil.METADATA_ELAN_TAG_OFFSET, NwConstants.NxmOfFieldType.OXM_OF_METADATA.getType(),
494                         MetaDataUtil.METADATA_ELAN_TAG_OFFSET, Ipv6ServiceConstants.ELAN_TAG_LENGTH),
495                 new ActionLearn.MatchFromValue(Icmpv6Type.NEIGHBOR_SOLICITATION.getValue(),
496                         NwConstants.NxmOfFieldType.OXM_OF_ICMPV6_TYPE.getType(),
497                         NwConstants.NxmOfFieldType.OXM_OF_ICMPV6_TYPE.getFlowModHeaderLenInt()),
498                 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.OXM_OF_IPV6_ND_TARGET.getType(),
499                         NwConstants.NxmOfFieldType.OXM_OF_IPV6_ND_TARGET.getType(),
500                         NwConstants.NxmOfFieldType.OXM_OF_IPV6_ND_TARGET.getFlowModHeaderLenInt()),
501                 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_NX_IPV6_SRC.getType(),
502                         NwConstants.NxmOfFieldType.NXM_NX_IPV6_SRC.getType(),
503                         NwConstants.NxmOfFieldType.NXM_NX_IPV6_SRC.getFlowModHeaderLenInt()),
504                 new ActionLearn.MatchFromField(NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
505                         NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getType(),
506                         NwConstants.NxmOfFieldType.NXM_NX_IPV6_DST.getFlowModHeaderLenInt()));
507         return new ActionLearn(0, hardTimeout, Ipv6ServiceConstants.SLOW_PATH_PROTECTION_PRIORITY,
508                 NwConstants.COOKIE_IPV6_TABLE, 0,
509                 NwConstants.IPV6_TABLE, 0, 0, flowMods);
510
511     }
512
513     public void instIcmpv6NsMatchFlow(short tableId, Uint64 dpId, Long elanTag, int lportTag, String vmMacAddress,
514                                       Ipv6Address ndTargetAddr, int addOrRemove, TypedReadWriteTransaction tx,
515                                       Boolean isSllOptionSet)
516         throws ExecutionException, InterruptedException  {
517
518         List<ActionInfo> actionsInfos = new ArrayList<>();
519         actionsInfos.add(getLearnActionForNsDrop(ipV6NAConfigHelper.getNsSlowProtectionTimeOutinMs()));
520         actionsInfos.add(new ActionSetIcmpv6Type(Icmpv6Type.NEIGHBOR_ADVERTISEMENT.getValue()));
521         short priority = Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY;
522         if (isSllOptionSet) {
523             actionsInfos.add(new ActionNdOptionType((short)2));
524             priority = Ipv6ServiceConstants.SLLOPTION_SET_FLOW_PRIORITY;
525         }
526         List<InstructionInfo> instructions = new ArrayList<>();
527         instructions.add(new InstructionApplyActions(actionsInfos));
528         instructions.add(new InstructionGotoTable(NwConstants.ARP_RESPONDER_TABLE));
529
530         List<MatchInfo> neighborSolicitationMatch = getIcmpv6NsMatchFlow(elanTag, lportTag, vmMacAddress,
531                 ndTargetAddr, isSllOptionSet);
532
533         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
534                 Boolean.TRUE.equals(isSllOptionSet)
535                         ? getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue(), vmMacAddress) :
536                         getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue()),priority,
537                 "IPv6NS", 0, 0, NwConstants.COOKIE_IPV6_TABLE,
538                 neighborSolicitationMatch, instructions);
539
540         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
541             LOG.debug("installIcmpv6NsResponderFlow: Removing IPv6 Neighbor Solicitation Flow on "
542                             + "DpId {} for NDTraget {}, elanTag {}, lportTag {}", dpId, ndTargetAddr.getValue(),
543                     elanTag, lportTag);
544
545             mdsalUtil.removeFlow(tx, rsFlowEntity);
546         } else {
547             LOG.debug("installIcmpv6NsResponderFlow: Installing IPv6 Neighbor Solicitation Flow on "
548                             + "DpId {} for NDTraget {} elanTag {}, lportTag {}", dpId, ndTargetAddr.getValue(),
549                     elanTag, lportTag);
550             mdsalUtil.addFlow(tx, rsFlowEntity);
551         }
552     }
553
554     public void installIcmpv6NaResponderFlow(short tableId, Uint64 dpId,
555                                              Long elanTag, int lportTag, IVirtualPort intf, Ipv6Address ndTargetAddr,
556                                              String rtrIntMacAddress, int addOrRemove,
557                                              TypedReadWriteTransaction tx, Boolean isTllOptionSet)
558         throws ExecutionException, InterruptedException {
559
560         List<MatchInfo> neighborAdvertisementMatch = getIcmpv6NaResponderMatch(elanTag, lportTag, intf.getMacAddress(),
561                 ndTargetAddr, isTllOptionSet);
562         short priority = isTllOptionSet ? Ipv6ServiceConstants.SLLOPTION_SET_FLOW_PRIORITY :
563                 Ipv6ServiceConstants.DEFAULT_FLOW_PRIORITY;
564         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
565             FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
566                     Boolean.TRUE.equals(isTllOptionSet)
567                             ? getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue(),
568                             intf.getMacAddress()) : getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue()),
569                     priority,"IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE,
570                     neighborAdvertisementMatch, null);
571             LOG.debug("installIcmpv6NaResponderFlow: Removing IPv6 Neighbor Advertisement Flow on "
572                             + "DpId {} for the NDTraget {}, elanTag {}, lportTag {}", dpId, ndTargetAddr.getValue(),
573                     elanTag, lportTag);
574
575             //mdsalUtil.removeFlowToTx(rsFlowEntity, tx);
576             mdsalUtil.removeFlow(tx, rsFlowEntity);
577         } else {
578             List<ActionInfo> actionsInfos = new ArrayList<>();
579             // Move Eth Src to Eth Dst
580             actionsInfos.add(new ActionMoveSourceDestinationEth());
581             actionsInfos.add(new ActionSetFieldEthernetSource(new MacAddress(rtrIntMacAddress)));
582
583             // Move Ipv6 Src to Ipv6 Dst
584             actionsInfos.add(new ActionMoveSourceDestinationIpv6());
585             actionsInfos.add(new ActionSetSourceIpv6(ndTargetAddr.getValue()));
586
587             actionsInfos.add(new ActionSetIpv6NdTarget(ndTargetAddr));
588             if (Boolean.TRUE.equals(isTllOptionSet)) {
589                 actionsInfos.add(new ActionSetIpv6NdTll(new MacAddress(rtrIntMacAddress)));
590                 actionsInfos.add(new ActionNdReserved(Long.parseLong("3758096384")));
591             } else {
592                 actionsInfos.add(new ActionNdReserved(Long.parseLong("3221225472")));
593             }
594             actionsInfos.add(new ActionNxLoadInPort(Uint64.ZERO));
595             actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
596
597             List<InstructionInfo> instructions = new ArrayList<>();
598             instructions.add(new InstructionApplyActions(actionsInfos));
599
600             FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
601                     Boolean.TRUE.equals(isTllOptionSet)
602                             ? getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue(),
603                             intf.getMacAddress()) : getIPv6OvsFlowRef(tableId, dpId, lportTag, ndTargetAddr.getValue()),
604                     priority,"IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE,
605                     neighborAdvertisementMatch, instructions);
606             LOG.debug("installIcmpv6NaResponderFlow: Installing IPv6 Neighbor Advertisement Flow on "
607                             + "DpId {} for the NDTraget {},  elanTag {}, lportTag {}", dpId, ndTargetAddr.getValue(),
608                     elanTag, lportTag);
609
610             mdsalUtil.addFlow(tx, rsFlowEntity);
611         }
612     }
613
614     public void installIcmpv6NsDefaultPuntFlow(short tableId, Uint64 dpId,  Long elanTag, Ipv6Address ipv6Address,
615                                                int addOrRemove, TypedReadWriteTransaction tx)
616         throws ExecutionException, InterruptedException {
617         List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
618         neighborSolicitationMatch.add(new MatchIpv6Source(UNSPECIFIED_ADDR.getValue() + "/128"));
619         List<InstructionInfo> instructions = new ArrayList<>();
620         List<ActionInfo> actionsInfos = new ArrayList<>();
621         actionsInfos.add(getLearnActionForNsDrop(ipV6NAConfigHelper.getNsSlowProtectionTimeOutinMs()));
622         actionsInfos.add(new ActionPuntToController());
623         instructions.add(new InstructionApplyActions(actionsInfos));
624         FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
625                 getIPv6FlowRef(dpId, elanTag, ipv6Address + ".UNSPECIFIED.Switch.NS.Responder"),
626                 Ipv6ServiceConstants.FLOW_SUBNET_PRIORITY , "IPv6NS",
627                 0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
628         if (addOrRemove == Ipv6ServiceConstants.DEL_FLOW) {
629             LOG.debug("installIcmpv6NsDefaultPuntFlow: Removing OVS based NA responder default subnet punt flow on "
630                     + "DpId {}, elanTag {} for Unspecified Address", dpId, elanTag);
631             mdsalUtil.removeFlow(tx, rsFlowEntity);
632
633         } else {
634             LOG.debug("installIcmpv6NsDefaultPuntFlow: Installing OVS based NA responder default subnet punt flow on "
635                     + "DpId {}, elanTag {} for Unspecified Address", dpId, elanTag);
636             mdsalUtil.addFlow(tx, rsFlowEntity);
637         }
638     }
639
640     private List<MatchInfo> getIcmpv6NsMatchFlow(Long elanTag, int lportTag, String vmMacAddress,
641                                                  Ipv6Address ndTargetAddr,
642                                                  Boolean isSllOptionSet) {
643         List<MatchInfo> matches = new ArrayList<>();
644         matches.add(MatchEthernetType.IPV6);
645         matches.add(MatchIpProtocol.ICMPV6);
646         matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_SOLICITATION.getValue(), (short) 0));
647         matches.add(new MatchIpv6NdTarget(new Ipv6Address(ndTargetAddr)));
648         if (Boolean.TRUE.equals(isSllOptionSet)) {
649             matches.add(new MatchNdOptionType((short)1));
650            /* matches.add(new MatchIpv6NdSll(new MacAddress(vmMacAddress))); */
651         }
652         matches.add(new MatchMetadata(ElanHelper.getElanMetadataLabel(elanTag, lportTag),
653                 ElanHelper.getElanMetadataMask()));
654         return matches;
655     }
656
657     private List<MatchInfo> getIcmpv6NaResponderMatch(Long elanTag, int lportTag, String vmMacAddress,
658                                                       Ipv6Address ndTarget, Boolean isNdOptionTypeSet) {
659         List<MatchInfo> matches = new ArrayList<>();
660         matches.add(MatchEthernetType.IPV6);
661         matches.add(MatchIpProtocol.ICMPV6);
662         matches.add(new MatchIcmpv6(Icmpv6Type.NEIGHBOR_ADVERTISEMENT.getValue(), (short) 0));
663         matches.add(new MatchIpv6NdTarget(new Ipv6Address(ndTarget)));
664         if (Boolean.TRUE.equals(isNdOptionTypeSet)) {
665             matches.add(new MatchNdOptionType((short)2));
666         }
667         matches.add(new MatchMetadata(ElanHelper.getElanMetadataLabel(elanTag, lportTag),
668                 ElanHelper.getElanMetadataMask()));
669         return matches;
670     }
671 }