NETVIRT-1586
[netvirt.git] / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / IfMgr.java
1 /*
2  * Copyright (c) 2015 Dell 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;
10
11 import com.google.common.base.Strings;
12 import com.google.common.collect.Lists;
13 import com.google.common.collect.Sets;
14 import com.google.common.net.InetAddresses;
15 import io.netty.util.Timeout;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Objects;
22 import java.util.Set;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Future;
28 import java.util.concurrent.TimeUnit;
29 import javax.annotation.PreDestroy;
30 import javax.inject.Inject;
31 import javax.inject.Singleton;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.opendaylight.genius.infra.Datastore;
34 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.genius.ipv6util.api.Ipv6Constants.Ipv6RouterAdvertisementType;
37 import org.opendaylight.genius.ipv6util.api.Ipv6Util;
38 import org.opendaylight.genius.mdsalutil.MDSALUtil;
39 import org.opendaylight.genius.mdsalutil.NwConstants;
40 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
41 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
42 import org.opendaylight.mdsal.binding.api.DataBroker;
43 import org.opendaylight.netvirt.elanmanager.api.IElanService;
44 import org.opendaylight.netvirt.ipv6service.api.ElementCache;
45 import org.opendaylight.netvirt.ipv6service.api.IVirtualNetwork;
46 import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
47 import org.opendaylight.netvirt.ipv6service.api.IVirtualRouter;
48 import org.opendaylight.netvirt.ipv6service.api.IVirtualSubnet;
49 import org.opendaylight.netvirt.ipv6service.utils.IpV6NAConfigHelper;
50 import org.opendaylight.netvirt.ipv6service.utils.Ipv6PeriodicTrQueue;
51 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceConstants;
52 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
53 import org.opendaylight.netvirt.ipv6service.utils.Ipv6TimerWheel;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
58 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
59 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
60 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.config.rev181010.Ipv6serviceConfig;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
73 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
74 import org.opendaylight.yangtools.yang.common.RpcResult;
75 import org.opendaylight.yangtools.yang.common.Uint64;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
78
79 @Singleton
80 public class IfMgr implements ElementCache, AutoCloseable {
81
82     private static final Logger LOG = LoggerFactory.getLogger(IfMgr.class);
83
84     private final ConcurrentMap<Uuid, VirtualRouter> vrouters = new ConcurrentHashMap<>();
85     private final ConcurrentMap<Uuid, VirtualNetwork> vnetworks = new ConcurrentHashMap<>();
86     private final ConcurrentMap<Uuid, VirtualSubnet> vsubnets = new ConcurrentHashMap<>();
87     private final ConcurrentMap<Uuid, VirtualPort> vintfs = new ConcurrentHashMap<>();
88     private final ConcurrentMap<Uuid, VirtualPort> vrouterv6IntfMap = new ConcurrentHashMap<>();
89     private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedRouterIntfs = new ConcurrentHashMap<>();
90     private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedSubnetIntfs = new ConcurrentHashMap<>();
91     private static ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedNetIntfs = new ConcurrentHashMap<>();
92     private static ConcurrentMap<Uuid, Integer> unprocessedNetRSFlowIntfs = new ConcurrentHashMap<>();
93     private static ConcurrentMap<Uuid, Set<Ipv6Address>> unprocessedNetNSFlowIntfs = new ConcurrentHashMap<>();
94     private static ConcurrentMap<Uuid, Set<VirtualSubnet>> unprocessedNetNaFlowIntfs = new ConcurrentHashMap<>();
95     private final OdlInterfaceRpcService interfaceManagerRpc;
96     private final IElanService elanProvider;
97     private final Ipv6ServiceUtils ipv6ServiceUtils;
98     private final IpV6NAConfigHelper ipV6NAConfigHelper;
99     private final DataBroker dataBroker;
100     private final Ipv6ServiceEosHandler ipv6ServiceEosHandler;
101     private final ManagedNewTransactionRunner txRunner;
102     private final PacketProcessingService packetService;
103     private final Ipv6PeriodicTrQueue ipv6Queue = new Ipv6PeriodicTrQueue(this::transmitUnsolicitedRA);
104     private final Ipv6TimerWheel timer = new Ipv6TimerWheel();
105     private final ExecutorService ndExecutorService = SpecialExecutors.newBlockingBoundedFastThreadPool(
106             10, Integer.MAX_VALUE, "NDExecutorService", IfMgr.class);
107     private final JobCoordinator coordinator;
108
109     @Inject
110     public IfMgr(DataBroker dataBroker, IElanService elanProvider, OdlInterfaceRpcService interfaceManagerRpc,
111                  PacketProcessingService packetService, Ipv6ServiceUtils ipv6ServiceUtils ,
112                  IpV6NAConfigHelper ipV6NAConfigHelper, final JobCoordinator coordinator,
113                  Ipv6ServiceEosHandler ipv6ServiceEosHandler) {
114         this.dataBroker = dataBroker;
115         this.elanProvider = elanProvider;
116         this.interfaceManagerRpc = interfaceManagerRpc;
117         this.packetService = packetService;
118         this.ipv6ServiceUtils = ipv6ServiceUtils;
119         this.ipV6NAConfigHelper = ipV6NAConfigHelper;
120         this.ipv6ServiceEosHandler = ipv6ServiceEosHandler;
121         this.coordinator = coordinator;
122         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
123         LOG.info("IfMgr is enabled");
124     }
125
126     @Override
127     @PreDestroy
128     public void close() {
129         ndExecutorService.shutdown();
130         timer.close();
131     }
132
133     public ExecutorService getNDExecutorService() {
134         return ndExecutorService;
135     }
136
137     /**
138      * Add router.
139      *
140      * @param rtrUuid router uuid
141      * @param rtrName router name
142      * @param tenantId tenant id
143      */
144     public void addRouter(Uuid rtrUuid, String rtrName, Uuid tenantId) {
145
146         VirtualRouter rtr = VirtualRouter.builder().routerUUID(rtrUuid).tenantID(tenantId).name(rtrName).build();
147         vrouters.put(rtrUuid, rtr);
148
149         Set<VirtualPort> intfList = unprocessedRouterIntfs.remove(rtrUuid);
150         if (intfList == null) {
151             LOG.debug("No unprocessed interfaces for the router {}", rtrUuid);
152             return;
153         }
154
155         synchronized (intfList) {
156             for (VirtualPort intf : intfList) {
157                 if (intf != null) {
158                     intf.setRouter(rtr);
159                     rtr.addInterface(intf);
160
161                     for (VirtualSubnet snet : intf.getSubnets()) {
162                         rtr.addSubnet(snet);
163                     }
164                 }
165             }
166         }
167     }
168
169     /**
170      * Remove Router.
171      *
172      * @param rtrUuid router uuid
173      */
174     public void removeRouter(Uuid rtrUuid) {
175
176         VirtualRouter rtr = rtrUuid != null ? vrouters.remove(rtrUuid) : null;
177         if (rtr != null) {
178             rtr.removeSelf();
179             removeUnprocessed(unprocessedRouterIntfs, rtrUuid);
180         } else {
181             LOG.error("Delete router failed for :{}", rtrUuid);
182         }
183     }
184
185     /**
186      * Add Subnet.
187      *
188      * @param snetId subnet id
189      * @param name subnet name
190      * @param tenantId tenant id
191      * @param gatewayIp gateway ip address
192      * @param ipVersion IP Version "IPv4 or IPv6"
193      * @param subnetCidr subnet CIDR
194      * @param ipV6AddressMode Address Mode of IPv6 Subnet
195      * @param ipV6RaMode RA Mode of IPv6 Subnet.
196      */
197     public void addSubnet(Uuid snetId, String name, Uuid tenantId,
198                           IpAddress gatewayIp, String ipVersion, IpPrefix subnetCidr,
199                           String ipV6AddressMode, String ipV6RaMode) {
200
201         // Save the gateway ipv6 address in its fully expanded format. We always store the v6Addresses
202         // in expanded form and are used during Neighbor Discovery Support.
203         if (gatewayIp != null) {
204             Ipv6Address addr = new Ipv6Address(InetAddresses
205                     .forString(gatewayIp.getIpv6Address().getValue()).getHostAddress());
206             gatewayIp = new IpAddress(addr);
207         }
208
209         VirtualSubnet snet = VirtualSubnet.builder().subnetUUID(snetId).tenantID(tenantId).name(name)
210                 .gatewayIp(gatewayIp).subnetCidr(subnetCidr).ipVersion(ipVersion).ipv6AddressMode(ipV6AddressMode)
211                 .ipv6RAMode(ipV6RaMode).build();
212
213         vsubnets.put(snetId, snet);
214
215         Set<VirtualPort> intfList = unprocessedSubnetIntfs.remove(snetId);
216         if (intfList == null) {
217             LOG.debug("No unprocessed interfaces for the subnet {}", snetId);
218             return;
219         }
220
221         synchronized (intfList) {
222             for (VirtualPort intf : intfList) {
223                 if (intf != null) {
224                     intf.setSubnet(snetId, snet);
225                     snet.addInterface(intf);
226
227                     VirtualRouter rtr = intf.getRouter();
228                     if (rtr != null) {
229                         rtr.addSubnet(snet);
230                     }
231                     updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
232                 }
233             }
234         }
235     }
236
237     /**
238      * Remove Subnet.
239      *
240      * @param snetId subnet id
241      */
242     public void removeSubnet(Uuid snetId) {
243         VirtualSubnet snet = snetId != null ? vsubnets.remove(snetId) : null;
244         if (snet != null) {
245             LOG.info("removeSubnet is invoked for {}", snetId);
246             snet.removeSelf();
247             removeUnprocessed(unprocessedSubnetIntfs, snetId);
248         }
249     }
250
251     @Nullable
252     private VirtualRouter getRouter(Uuid rtrId) {
253         return rtrId != null ? vrouters.get(rtrId) : null;
254     }
255
256     public void addRouterIntf(Uuid portId, Uuid rtrId, Uuid snetId,
257                               Uuid networkId, IpAddress fixedIp, String macAddress,
258                               String deviceOwner) {
259         LOG.debug("addRouterIntf portId {}, rtrId {}, snetId {}, networkId {}, ip {}, mac {}",
260                 portId, rtrId, snetId, networkId, fixedIp, macAddress);
261         /* Added the below logic for supporting neutron router interface creation.
262          * Since when neutron port is created with fixed Ipv6 address, that time it will be
263          * treated as a host port and it will be added into the vintfs map through
264          * NeutronPortChangeListener ADD() event.
265          * Later the same neutron port is added to router this time it will be treated as
266          * a router_interface through NeutronPortChangeListener UPDATE() event.
267          */
268         VirtualPort virInterface = vintfs.get(portId);
269         if (virInterface != null && Strings.isNullOrEmpty(virInterface.getDeviceOwner())) {
270             vintfs.remove(portId);
271         }
272         //Save the interface ipv6 address in its fully expanded format
273         Ipv6Address addr = new Ipv6Address(InetAddresses
274                 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
275         fixedIp = new IpAddress(addr);
276
277         VirtualPort intf = VirtualPort.builder().intfUUID(portId).networkID(networkId).macAddress(macAddress)
278                 .routerIntfFlag(true).deviceOwner(deviceOwner).build();
279         intf.setSubnetInfo(snetId, fixedIp);
280         intf.setPeriodicTimer(ipv6Queue);
281         int networkMtu = getNetworkMtu(networkId);
282         if (networkMtu != 0) {
283             intf.setMtu(networkMtu);
284         }
285
286         boolean newIntf = false;
287         VirtualPort prevIntf = vintfs.putIfAbsent(portId, intf);
288         if (prevIntf == null) {
289             newIntf = true;
290             MacAddress ifaceMac = MacAddress.getDefaultInstance(macAddress);
291             Ipv6Address llAddr = Ipv6Util.getIpv6LinkLocalAddressFromMac(ifaceMac);
292             /* A new router interface is created. This is basically triggered when an
293             IPv6 subnet is associated to the router. Check if network is already hosting
294             any VMs. If so, on all the hosts that have VMs on the network, program the
295             icmpv6 punt flows in IPV6_TABLE(45).
296              */
297             programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.ADD_FLOW);
298             if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
299                 checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.ADD_FLOW);
300             }
301             else {
302                 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), llAddr, Ipv6ServiceConstants.ADD_FLOW);
303             }
304             programIcmpv6NaForwardFlows(intf.getNetworkID(), getSubnet(snetId), Ipv6ServiceConstants.ADD_FLOW);
305         } else {
306             intf = prevIntf;
307             intf.setSubnetInfo(snetId, fixedIp);
308         }
309
310         VirtualRouter rtr = getRouter(rtrId);
311         VirtualSubnet snet = getSubnet(snetId);
312
313         if (rtr != null && snet != null) {
314             snet.setRouter(rtr);
315             intf.setSubnet(snetId, snet);
316             rtr.addSubnet(snet);
317         } else if (snet != null) {
318             intf.setSubnet(snetId, snet);
319             addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
320         } else {
321             addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
322             addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
323         }
324
325         if (networkId != null) {
326             LOG.trace("Adding vrouterv6IntfMap for network:{} intf:{}", networkId, intf);
327             vrouterv6IntfMap.put(networkId, intf);
328         }
329
330         if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Controller)) {
331             programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), fixedIp.getIpv6Address(),
332                     Ipv6ServiceConstants.ADD_FLOW);
333         }
334         programIcmpv6NaPuntFlow(networkId, intf.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
335
336         if (newIntf) {
337             LOG.debug("start the periodic RA Timer for routerIntf {}", portId);
338             transmitUnsolicitedRA(intf);
339         }
340     }
341
342     public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList, Set<FixedIps> deletedIps) {
343         LOG.info("updateRouterIntf portId {}, fixedIpsList {} ", portId, fixedIpsList);
344         VirtualPort intf = getPort(portId);
345         if (intf == null) {
346             LOG.info("Skip Router interface update for non-ipv6 port {}", portId);
347             return;
348         }
349         Uuid networkID = intf.getNetworkID();
350         intf.clearSubnetInfo();
351         for (FixedIps fip : fixedIpsList) {
352             IpAddress fixedIp = fip.getIpAddress();
353
354             if (fixedIp.getIpv4Address() != null) {
355                 continue;
356             }
357
358             //Save the interface ipv6 address in its fully expanded format
359             Ipv6Address addr = new Ipv6Address(InetAddresses
360                     .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
361             fixedIp = new IpAddress(addr);
362             Uuid subnetId = fip.getSubnetId();
363             intf.setSubnetInfo(subnetId, fixedIp);
364
365             VirtualRouter rtr = getRouter(rtrId);
366             VirtualSubnet snet = getSubnet(subnetId);
367
368             if (rtr != null && snet != null) {
369                 snet.setRouter(rtr);
370                 intf.setSubnet(subnetId, snet);
371                 rtr.addSubnet(snet);
372             } else if (snet != null) {
373                 intf.setSubnet(subnetId, snet);
374                 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
375             } else {
376                 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
377                 addUnprocessed(unprocessedSubnetIntfs, subnetId, intf);
378             }
379
380             if (networkID != null) {
381                 vrouterv6IntfMap.put(networkID, intf);
382             }
383         }
384
385         /*
386          * This is a port update event for routerPort. Check if any IPv6 subnet is added or removed from the
387          * router port. Depending on subnet added/removed, we add/remove the corresponding flows from
388          * IPV6_TABLE(45). Add is handled in addInterfaceInfo(), delete case is handled here.
389          */
390         for (FixedIps ips : deletedIps) {
391             VirtualSubnet snet = getSubnet(ips.getSubnetId());
392             programIcmpv6NaPuntFlow(networkID, Lists.newArrayList(snet), Ipv6ServiceConstants.DEL_FLOW);
393             if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
394                 checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
395             } else {
396                 programIcmpv6NSPuntFlowForAddress(networkID, ips.getIpAddress().getIpv6Address(),
397                         Ipv6ServiceConstants.DEL_FLOW);
398             }
399         }
400     }
401
402     @Nullable
403     private VirtualSubnet getSubnet(Uuid snetId) {
404         return snetId != null ? vsubnets.get(snetId) : null;
405     }
406
407     public void addHostIntf(Uuid portId, Uuid snetId, Uuid networkId,
408                             IpAddress fixedIp, String macAddress, String deviceOwner) {
409         LOG.debug("addHostIntf portId {}, snetId {}, networkId {}, ip {}, mac {}",
410                 portId, snetId, networkId, fixedIp, macAddress);
411
412         //Save the interface ipv6 address in its fully expanded format
413         Ipv6Address addr = new Ipv6Address(InetAddresses
414                 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
415         fixedIp = new IpAddress(addr);
416
417         VirtualPort intf = VirtualPort.builder().intfUUID(portId).networkID(networkId).macAddress(macAddress)
418                 .routerIntfFlag(false).deviceOwner(deviceOwner).build();
419         intf.setSubnetInfo(snetId, fixedIp);
420
421         VirtualPort prevIntf = vintfs.putIfAbsent(portId, intf);
422         if (prevIntf == null) {
423             Long elanTag = getNetworkElanTag(networkId);
424             final VirtualPort virtIntf = intf;
425             String jobKey = Ipv6ServiceUtils.buildIpv6MonitorJobKey(portId.getValue());
426             coordinator.enqueueJob(jobKey, () -> {
427                 // Do service binding for the port and set the serviceBindingStatus to true.
428                 ipv6ServiceUtils.bindIpv6Service(portId.getValue(), elanTag, NwConstants.IPV6_TABLE);
429                 virtIntf.setServiceBindingStatus(true);
430
431                 /* Update the intf dpnId/ofPort from the Operational Store */
432                 updateInterfaceDpidOfPortInfo(portId);
433
434                 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
435                     checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, virtIntf, Ipv6ServiceConstants.ADD_FLOW);
436                 }
437                 return Collections.emptyList();
438             });
439         } else {
440             intf = prevIntf;
441             intf.setSubnetInfo(snetId, fixedIp);
442         }
443
444         VirtualSubnet snet = getSubnet(snetId);
445
446         if (snet != null) {
447             intf.setSubnet(snetId, snet);
448         } else {
449             addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
450         }
451     }
452
453     @Nullable
454     private VirtualPort getPort(Uuid portId) {
455         return portId != null ? vintfs.get(portId) : null;
456     }
457
458     public void clearAnyExistingSubnetInfo(Uuid portId) {
459         VirtualPort intf = getPort(portId);
460         if (intf != null) {
461             intf.clearSubnetInfo();
462         }
463     }
464
465     public void updateHostIntf(Uuid portId, Boolean portIncludesV6Address) {
466         VirtualPort intf = getPort(portId);
467         if (intf == null) {
468             LOG.debug("Update Host interface failed. Could not get Host interface details {}", portId);
469             return;
470         }
471
472         /* If the VMPort initially included an IPv6 address (along with IPv4 address) and IPv6 address
473          was removed, we will have to unbind the service on the VM port. Similarly we do a ServiceBind
474          if required.
475           */
476         String jobKey = Ipv6ServiceUtils.buildIpv6MonitorJobKey(portId.getValue());
477         coordinator.enqueueJob(jobKey, () -> {
478             if (portIncludesV6Address) {
479                 if (!intf.getServiceBindingStatus()) {
480                     Long elanTag = getNetworkElanTag(intf.getNetworkID());
481                     LOG.info("In updateHostIntf, service binding for portId {}", portId);
482                     ipv6ServiceUtils.bindIpv6Service(portId.getValue(), elanTag, NwConstants.IPV6_TABLE);
483                     intf.setServiceBindingStatus(true);
484                     if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(),
485                             Ipv6serviceConfig.NaResponderMode.Switch)) {
486                         LOG.debug("In updateHostIntf: Host Interface {}", intf);
487                         checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.ADD_FLOW);
488                     }
489                 }
490             } else {
491                 LOG.info("In updateHostIntf, removing service binding for portId {}", portId);
492                 ipv6ServiceUtils.unbindIpv6Service(portId.getValue());
493                 intf.setServiceBindingStatus(false);
494                 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
495                     LOG.debug("In updateHostIntf: Host Interface {}", intf);
496                     checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
497                 }
498             }
499             return Collections.emptyList();
500         });
501     }
502
503     public void updateDpnInfo(Uuid portId, Uint64 dpId, Long ofPort) {
504         LOG.info("In updateDpnInfo portId {}, dpId {}, ofPort {}",
505                 portId, dpId, ofPort);
506         VirtualPort intf = getPort(portId);
507         if (intf != null) {
508             intf.setDpId(dpId);
509             intf.setOfPort(ofPort);
510
511             // Update the network <--> List[dpnIds, List<ports>] cache.
512             VirtualNetwork vnet = getNetwork(intf.getNetworkID());
513             if (null != vnet) {
514                 vnet.updateDpnPortInfo(dpId, ofPort, portId, Ipv6ServiceConstants.ADD_ENTRY);
515             } else {
516                 LOG.error("In updateDpnInfo networks NOT FOUND: networkID {}, portId {}, dpId {}, ofPort {}",
517                            intf.getNetworkID(), portId, dpId, ofPort);
518                 addUnprocessed(unprocessedNetIntfs, intf.getNetworkID(), intf);
519             }
520         } else {
521             LOG.error("In updateDpnInfo port NOT FOUND: portId {}, dpId {}, ofPort {}",
522                     portId, dpId, ofPort);
523         }
524     }
525
526
527     public void updateInterfaceDpidOfPortInfo(Uuid portId) {
528         LOG.debug("In updateInterfaceDpidOfPortInfo portId {}", portId);
529         Interface interfaceState = ipv6ServiceUtils.getInterfaceStateFromOperDS(portId.getValue());
530         if (interfaceState == null) {
531             LOG.warn("In updateInterfaceDpidOfPortInfo, port info not found in Operational Store {}.", portId);
532             return;
533         }
534
535         List<String> ofportIds = interfaceState.getLowerLayerIf();
536         NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
537         Uint64 dpId = ipv6ServiceUtils.getDpIdFromInterfaceState(interfaceState);
538         if (!dpId.equals(Ipv6ServiceConstants.INVALID_DPID)) {
539             Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
540             updateDpnInfo(portId, dpId, ofPort);
541         }
542     }
543
544
545     public void removePort(Uuid portId) {
546         VirtualPort intf = portId != null ? vintfs.remove(portId) : null;
547         if (intf != null) {
548             intf.removeSelf();
549             Uuid networkID = intf.getNetworkID();
550             if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_INTERFACE)) {
551                 LOG.info("In removePort for router interface, portId {}", portId);
552
553                 if (networkID != null) {
554                     vrouterv6IntfMap.remove(networkID, intf);
555                 }
556
557                 /* Router port is deleted. Remove the corresponding icmpv6 punt flows on all
558                 the dpnIds which were hosting the VMs on the network.
559                  */
560                 programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.DEL_FLOW);
561                 programIcmpv6NaPuntFlow(networkID, intf.getSubnets(), Ipv6ServiceConstants.DEL_FLOW);
562
563                 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
564                     checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
565                 } else {
566                     for (Ipv6Address ipv6Address : intf.getIpv6Addresses()) {
567                         programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address,
568                                 Ipv6ServiceConstants.DEL_FLOW);
569                     }
570                 }
571
572                 for (VirtualSubnet subnet : intf.getSubnets()) {
573                     programIcmpv6NaForwardFlows(networkID, subnet, Ipv6ServiceConstants.DEL_FLOW);
574                 }
575             } else {
576                 LOG.info("In removePort for host interface, portId {}", portId);
577                 String jobKey = Ipv6ServiceUtils.buildIpv6MonitorJobKey(portId.getValue());
578                 coordinator.enqueueJob(jobKey, () -> {
579                     // Remove the serviceBinding entry for the port.
580                     ipv6ServiceUtils.unbindIpv6Service(portId.getValue());
581                     // Remove the portId from the (network <--> List[dpnIds, List <ports>]) cache.
582                     VirtualNetwork vnet = getNetwork(networkID);
583                     if (null != vnet) {
584                         Uint64 dpId = intf.getDpId();
585                         vnet.updateDpnPortInfo(dpId, intf.getOfPort(), portId, Ipv6ServiceConstants.DEL_ENTRY);
586                     }
587                     if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(),
588                             Ipv6serviceConfig.NaResponderMode.Switch)) {
589                         if (intf.getDpId() != null) {
590                             checkIcmpv6NsMatchAndResponderFlow(intf.getDpId(), 0, intf, Ipv6ServiceConstants.DEL_FLOW);
591                         } else {
592                             checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
593                         }
594                     }
595                     return Collections.emptyList();
596                 });
597             }
598             transmitRouterAdvertisement(intf, Ipv6RouterAdvertisementType.CEASE_ADVERTISEMENT);
599             timer.cancelPeriodicTransmissionTimeout(intf.getPeriodicTimeout());
600             intf.resetPeriodicTimeout();
601             LOG.debug("Reset the periodic RA Timer for intf {}", intf.getIntfUUID());
602         }
603     }
604
605     public void deleteInterface(Uuid interfaceUuid, String dpId) {
606         // Nothing to do for now
607     }
608
609     public void addUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id, VirtualPort intf) {
610         if (id != null) {
611             unprocessed.computeIfAbsent(id,
612                 key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet())).add(intf);
613         }
614     }
615
616     @Nullable
617     public Set<VirtualPort> removeUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id) {
618         if (id != null) {
619             return unprocessed.remove(id);
620         }
621         return null;
622     }
623
624     public void addUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id, Integer action) {
625         unprocessed.put(id, action);
626
627     }
628
629     public Integer removeUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id) {
630         return unprocessed.remove(id);
631     }
632
633     private void addUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed, Uuid id, Ipv6Address ipv6Address,
634             Integer action) {
635         if (action == Ipv6ServiceConstants.ADD_FLOW) {
636             unprocessed.computeIfAbsent(id, key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet()))
637                     .add(ipv6Address);
638         } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
639             Set<Ipv6Address> ipv6AddressesList = unprocessed.get(id);
640             if ((ipv6AddressesList != null) && (ipv6AddressesList.contains(ipv6Address))) {
641                 ipv6AddressesList.remove(ipv6Address);
642             }
643         }
644     }
645
646     private Set<Ipv6Address> removeUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed, Uuid id) {
647         Set<Ipv6Address> removedIps = unprocessed.remove(id);
648         return removedIps != null ? removedIps : Collections.emptySet();
649     }
650
651     private void addUnprocessedNaFlows(Uuid networkId, Collection<VirtualSubnet> subnets, Integer action) {
652         if (action == Ipv6ServiceConstants.ADD_FLOW) {
653             unprocessedNetNaFlowIntfs.computeIfAbsent(networkId, key -> ConcurrentHashMap.newKeySet())
654                     .addAll(subnets);
655         } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
656             Set<VirtualSubnet> subnetsInCache = unprocessedNetNaFlowIntfs.get(networkId);
657             if (subnetsInCache != null) {
658                 subnetsInCache.removeAll(subnets);
659             }
660         }
661     }
662
663     private Set<VirtualSubnet> removeUnprocessedNaFlows(Uuid networkId) {
664         Set<VirtualSubnet> removedSubnets = unprocessedNetNaFlowIntfs.remove(networkId);
665         return removedSubnets != null ? removedSubnets : Collections.emptySet();
666     }
667
668     @Nullable
669     public VirtualPort getRouterV6InterfaceForNetwork(Uuid networkId) {
670         LOG.debug("obtaining the virtual interface for {}", networkId);
671         return networkId != null ? vrouterv6IntfMap.get(networkId) : null;
672     }
673
674     @Nullable
675     public VirtualPort obtainV6Interface(Uuid id) {
676         VirtualPort intf = getPort(id);
677         if (intf == null) {
678             return null;
679         }
680         for (VirtualSubnet snet : intf.getSubnets()) {
681             if (snet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6)) {
682                 return intf;
683             }
684         }
685         return null;
686     }
687
688     private void programIcmpv6RSPuntFlows(Uuid networkId, int action) {
689         if (!ipv6ServiceEosHandler.isClusterOwner()) {
690             LOG.trace("Not a cluster Owner, skip flow programming.");
691             return;
692         }
693
694         Long elanTag = getNetworkElanTag(networkId);
695         int flowStatus;
696         VirtualNetwork vnet = getNetwork(networkId);
697         if (vnet != null) {
698             List<Uint64> dpnList = vnet.getDpnsHostingNetwork();
699             for (Uint64 dpId : dpnList) {
700                 flowStatus = vnet.getRSPuntFlowStatusOnDpnId(dpId);
701                 if (action == Ipv6ServiceConstants.ADD_FLOW
702                         && flowStatus == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
703                     ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
704                             Ipv6ServiceConstants.ADD_FLOW);
705                     vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
706                 } else if (action == Ipv6ServiceConstants.DEL_FLOW
707                         && flowStatus == Ipv6ServiceConstants.FLOWS_CONFIGURED) {
708                     ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
709                             Ipv6ServiceConstants.DEL_FLOW);
710                     vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED);
711                 }
712             }
713         } else {
714             addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, action);
715         }
716     }
717
718     protected void programIcmpv6NSPuntFlowForAddress(Uuid networkId, Ipv6Address ipv6Address, int action) {
719         if (!ipv6ServiceEosHandler.isClusterOwner()) {
720             LOG.trace("Not a cluster Owner, skip flow programming.");
721             return;
722         }
723
724         Long elanTag = getNetworkElanTag(networkId);
725         VirtualNetwork vnet = getNetwork(networkId);
726         if (vnet != null) {
727             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
728             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
729                 if (action == Ipv6ServiceConstants.ADD_FLOW
730                         && !dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)
731                         && dpnIfaceInfo.getDpId() != null) {
732                     ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
733                             ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
734                     dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
735                 } else if (action == Ipv6ServiceConstants.DEL_FLOW
736                         && dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
737                     ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
738                             ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
739                     dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
740                 }
741             }
742         } else {
743             addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, action);
744         }
745     }
746
747     private void programIcmpv6NaPuntFlow(Uuid networkID, Collection<VirtualSubnet> subnets, int action) {
748         if (!ipv6ServiceEosHandler.isClusterOwner()) {
749             LOG.trace("Not a cluster Owner, skip flow programming.");
750             return;
751         }
752         Long elanTag = getNetworkElanTag(networkID);
753         VirtualNetwork vnet = getNetwork(networkID);
754         if (vnet != null) {
755             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
756             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
757                 if (dpnIfaceInfo.getDpId() == null) {
758                     continue;
759                 }
760                 for (VirtualSubnet subnet : subnets) {
761                     Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
762                     if (ipv6SubnetPrefix != null) {
763                         if (action == Ipv6ServiceConstants.ADD_FLOW
764                                 && !dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
765                             ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
766                                     dpnIfaceInfo.getDpId(), elanTag, action);
767                             dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
768                         } else if (action == Ipv6ServiceConstants.DEL_FLOW
769                                 && dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
770                             ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
771                                     dpnIfaceInfo.getDpId(), elanTag, action);
772                             dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
773                         }
774                     }
775                 }
776             }
777         } else {
778             addUnprocessedNaFlows(networkID, subnets, action);
779         }
780     }
781
782     public void handleInterfaceStateEvent(VirtualPort port, Uint64 dpId, VirtualPort routerPort,int lportTag ,
783                                           int addOrRemove) {
784         Long elanTag = getNetworkElanTag(port.getNetworkID());
785         if (addOrRemove == Ipv6ServiceConstants.ADD_FLOW && routerPort != null) {
786             // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
787             programIcmpv6PuntFlows(port, dpId, elanTag, routerPort, lportTag);
788         }
789
790         if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
791             checkIcmpv6NsMatchAndResponderFlow(dpId, lportTag, port, addOrRemove);
792         }
793
794         if (elanTag != null) {
795             programIcmpv6NaForwardFlow(port, dpId, elanTag, addOrRemove);
796         } else {
797             addUnprocessedNaFlows(port.getNetworkID(), port.getSubnets(), addOrRemove);
798         }
799     }
800
801     private void programIcmpv6PuntFlows(IVirtualPort vmPort, Uint64 dpId, Long elanTag, VirtualPort routerPort,
802                                         int lportTag) {
803         Uuid networkId = vmPort.getNetworkID();
804         VirtualNetwork vnet = getNetwork(networkId);
805         if (null != vnet) {
806             VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
807             if (null != dpnInfo) {
808                 if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
809                     ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
810                             Ipv6ServiceConstants.ADD_FLOW);
811                     vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
812                 }
813
814                 for (VirtualSubnet subnet : routerPort.getSubnets()) {
815                     Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
816                     if (ipv6SubnetPrefix != null
817                             && !dpnInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
818                         ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix, dpId,
819                                 elanTag, Ipv6ServiceConstants.ADD_FLOW);
820                         dpnInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), Ipv6ServiceConstants.ADD_FLOW);
821                     }
822                 }
823
824                 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(),
825                         Ipv6serviceConfig.NaResponderMode.Controller)) {
826                     for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
827                         if (!dpnInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
828                             ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
829                                     elanTag, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
830                             dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
831                         }
832                     }
833                 }
834             }
835         } else {
836             addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, Ipv6ServiceConstants.ADD_FLOW);
837             for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
838                 addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
839             }
840             addUnprocessedNaFlows(networkId, routerPort.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
841         }
842     }
843
844     private void programIcmpv6NaForwardFlows(Uuid networkId, VirtualSubnet subnet, int action) {
845         if (!ipv6ServiceEosHandler.isClusterOwner()) {
846             LOG.trace("Not a cluster Owner, skip flow programming.");
847             return;
848         }
849         if (subnet == null) {
850             LOG.debug("Subnet is null, skipping programIcmpv6NaForwardFlows.");
851             return;
852         }
853
854         VirtualNetwork vnet = getNetwork(networkId);
855         if (vnet != null) {
856             if (!Ipv6ServiceUtils.isIpv6Subnet(subnet)) {
857                 return;
858             }
859             Long elanTag = getNetworkElanTag(networkId);
860             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
861             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
862                 if (dpnIfaceInfo.getDpId() == null) {
863                     continue;
864                 }
865                 List<VirtualPort> vmPorts = getVmPortsInSubnetByDpId(subnet.getSubnetUUID(), dpnIfaceInfo.getDpId());
866                 for (VirtualPort vmPort : vmPorts) {
867                     programIcmpv6NaForwardFlow(vmPort, dpnIfaceInfo.getDpId(), elanTag, action);
868                 }
869             }
870         } else {
871             addUnprocessedNaFlows(networkId, Sets.newHashSet(subnet), action);
872         }
873     }
874
875     /**
876      * Programs ICMPv6 NA forwarding flow for fixed IPs of neutron port. NA's from non-fixed IPs are
877      * punted to controller for learning.
878      *
879      * @param vmPort the VM port
880      * @param dpId the DP ID
881      * @param elanTag the ELAN tag
882      * @param addOrRemove the add or remove flag
883      */
884     private void programIcmpv6NaForwardFlow(IVirtualPort vmPort, Uint64 dpId, Long elanTag, int addOrRemove) {
885         ipv6ServiceUtils.installIcmpv6NaForwardFlow(NwConstants.IPV6_TABLE, vmPort, dpId, elanTag, addOrRemove);
886     }
887
888     public List<VirtualPort> getVmPortsInSubnetByDpId(Uuid snetId, Uint64 dpId) {
889         List<VirtualPort> vmPorts = new ArrayList<>();
890         for (VirtualPort port : vintfs.values()) {
891             if (dpId.equals(port.getDpId()) && Ipv6ServiceUtils.isVmPort(port.getDeviceOwner())) {
892                 for (VirtualSubnet subnet : port.getSubnets()) {
893                     if (subnet.getSubnetUUID().equals(snetId)) {
894                         vmPorts.add(port);
895                     }
896                 }
897             }
898         }
899         return vmPorts;
900     }
901
902     public String getInterfaceNameFromTag(long portTag) {
903         String interfaceName = null;
904         GetInterfaceFromIfIndexInput input = new GetInterfaceFromIfIndexInputBuilder()
905                 .setIfIndex((int) portTag).build();
906         Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
907                 interfaceManagerRpc.getInterfaceFromIfIndex(input);
908         try {
909             GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
910             interfaceName = output.getInterfaceName();
911         } catch (InterruptedException | ExecutionException e) {
912             LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
913         }
914         LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
915         return interfaceName;
916     }
917
918     @Nullable
919     public Long updateNetworkElanTag(Uuid networkId) {
920         Long elanTag = null;
921         if (null != this.elanProvider) {
922             ElanInstance elanInstance = this.elanProvider.getElanInstance(networkId.getValue());
923             if (null != elanInstance) {
924                 elanTag = elanInstance.getElanTag().longValue();
925                 VirtualNetwork net = getNetwork(networkId);
926                 if (null != net) {
927                     net.setElanTag(elanTag);
928                 }
929             }
930         }
931         return elanTag;
932     }
933
934     public void updateNetworkMtuInfo(Uuid networkId, int mtu) {
935         VirtualNetwork net = getNetwork(networkId);
936         if (null != net) {
937             net.setMtu(mtu);
938         }
939     }
940
941     @Nullable
942     public VirtualNetwork getNetwork(Uuid networkId) {
943         return networkId != null ? vnetworks.get(networkId) : null;
944     }
945
946     @Nullable
947     public Long getNetworkElanTag(Uuid networkId) {
948         Long elanTag = null;
949         IVirtualNetwork net = getNetwork(networkId);
950         if (null != net) {
951             elanTag = net.getElanTag();
952             if (null == elanTag) {
953                 elanTag = updateNetworkElanTag(networkId);
954             }
955         }
956         return elanTag;
957     }
958
959     @Nullable
960     public int getNetworkMtu(Uuid networkId) {
961         int mtu = 0;
962         IVirtualNetwork net = getNetwork(networkId);
963         if (null != net) {
964             mtu = net.getMtu();
965         }
966         return mtu;
967     }
968
969     public void addNetwork(Uuid networkId, int mtu) {
970         if (networkId == null) {
971             return;
972         }
973
974         if (vnetworks.putIfAbsent(networkId, new VirtualNetwork(networkId)) == null) {
975             updateNetworkMtuInfo(networkId, mtu);
976             updateNetworkElanTag(networkId);
977         }
978         Set<VirtualPort> intfList = removeUnprocessed(unprocessedNetIntfs, networkId);
979         if (intfList == null) {
980             LOG.info("No unprocessed interfaces for the net {}", networkId);
981             return;
982         }
983
984         for (VirtualPort intf : intfList) {
985             if (intf != null) {
986                 updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
987             }
988         }
989
990         Set<Ipv6Address> ipv6Addresses =
991                 removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
992
993         for (Ipv6Address ipv6Address : ipv6Addresses) {
994             programIcmpv6NSPuntFlowForAddress(networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
995         }
996
997         Integer action = removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
998         programIcmpv6RSPuntFlows(networkId, action);
999
1000         Set<VirtualSubnet> subnets = removeUnprocessedNaFlows(networkId);
1001         programIcmpv6NaPuntFlow(networkId, subnets, Ipv6ServiceConstants.ADD_FLOW);
1002         for (VirtualSubnet subnet : subnets) {
1003             programIcmpv6NaForwardFlows(networkId, subnet, action);
1004         }
1005     }
1006
1007     public void removeNetwork(Uuid networkId) {
1008         // Delete the network and the corresponding dpnIds<-->List(ports) cache.
1009         VirtualNetwork net = networkId != null ? vnetworks.remove(networkId) : null;
1010         if (null != net && null != net.getNetworkUuid()) {
1011             /* removing all RS flows when network gets removed, as the DPN-list is maintained only as part of
1012             * network cache. After removal of network there is no way to remove them today. */
1013             programIcmpv6RSPuntFlows(net.getNetworkUuid(), Ipv6ServiceConstants.DEL_FLOW);
1014             removeAllIcmpv6NSPuntFlowForNetwork(net.getNetworkUuid());
1015             net.removeSelf();
1016         }
1017         removeUnprocessed(unprocessedNetIntfs, networkId);
1018         removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
1019         removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
1020     }
1021
1022     private void transmitRouterAdvertisement(VirtualPort intf, Ipv6RouterAdvertisementType advType) {
1023         Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt(packetService, this);
1024
1025         VirtualNetwork vnet = getNetwork(intf.getNetworkID());
1026         if (vnet != null) {
1027             long elanTag = vnet.getElanTag();
1028             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
1029             /* For UNSOLICITED_ADVERTISEMENT and CEASE_ADVERTISEMENT invoking transmitRtrAdvertisement()
1030              * one time is sufficient. Since using IPv6 network ELAN BC group it will send an RA packetOut
1031              * information to all VMs OF port which are booted on that particular ELAN Group
1032              */
1033             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1034                 LOG.debug("transmitRouterAdvertisement: Transmitting RA {} for ELAN Tag {}",
1035                         advType, elanTag);
1036                 if (dpnIfaceInfo.getDpId() != null) {
1037                     ipv6RouterAdvert.transmitRtrAdvertisement(advType, intf, elanTag, null,
1038                             dpnIfaceInfo.getDpId(), intf.getIntfUUID(),
1039                             ipV6NAConfigHelper.getIpv6RouterReachableTimeinMS());
1040                     break;
1041                 }
1042             }
1043         }
1044     }
1045
1046
1047     private void removeAllIcmpv6NSPuntFlowForNetwork(Uuid networkId) {
1048         Long elanTag = getNetworkElanTag(networkId);
1049         VirtualNetwork vnet = vnetworks.get(networkId);
1050         if (vnet == null) {
1051             return;
1052         }
1053
1054         Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
1055
1056         for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1057             for (Ipv6Address ipv6Address : dpnIfaceInfo.ndTargetFlowsPunted) {
1058                 if (ipv6ServiceEosHandler.isClusterOwner()) {
1059                     ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
1060                             elanTag, ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
1061                 }
1062                 dpnIfaceInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.DEL_ENTRY);
1063             }
1064         }
1065     }
1066
1067
1068     public void transmitUnsolicitedRA(Uuid portId) {
1069         VirtualPort port = getPort(portId);
1070         LOG.debug("in transmitUnsolicitedRA for {}, port {}", portId, port);
1071         if (port != null) {
1072             transmitUnsolicitedRA(port);
1073         }
1074     }
1075
1076     public void transmitUnsolicitedRA(VirtualPort port) {
1077         if (ipv6ServiceEosHandler.isClusterOwner()) {
1078             /* Only the Cluster Owner would be sending out the Periodic RAs.
1079                However, the timer is configured on all the nodes to handle cluster fail-over scenarios.
1080              */
1081             transmitRouterAdvertisement(port, Ipv6RouterAdvertisementType.UNSOLICITED_ADVERTISEMENT);
1082         }
1083         Timeout portTimeout = timer.setPeriodicTransmissionTimeout(port.getPeriodicTimer(),
1084                 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL,
1085                 TimeUnit.SECONDS);
1086         port.setPeriodicTimeout(portTimeout);
1087         LOG.debug("re-started periodic RA Timer for routerIntf {}, int {}s", port.getIntfUUID(),
1088                 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL);
1089     }
1090
1091     @Override
1092     public List<IVirtualPort> getInterfaceCache() {
1093         List<IVirtualPort> virtualPorts = new ArrayList<>();
1094         for (VirtualPort vport: vintfs.values()) {
1095             virtualPorts.add(vport);
1096         }
1097         return virtualPorts;
1098     }
1099
1100     @Override
1101     public List<IVirtualNetwork> getNetworkCache() {
1102         List<IVirtualNetwork> virtualNetworks = new ArrayList<>();
1103         for (VirtualNetwork vnet: vnetworks.values()) {
1104             virtualNetworks.add(vnet);
1105         }
1106         return virtualNetworks;
1107     }
1108
1109     @Override
1110     public List<IVirtualSubnet> getSubnetCache() {
1111         List<IVirtualSubnet> virtualSubnets = new ArrayList<>();
1112         for (VirtualSubnet vsubnet: vsubnets.values()) {
1113             virtualSubnets.add(vsubnet);
1114         }
1115         return virtualSubnets;
1116     }
1117
1118     @Override
1119     public List<IVirtualRouter> getRouterCache() {
1120         List<IVirtualRouter> virtualRouter = new ArrayList<>();
1121         for (VirtualRouter vrouter: vrouters.values()) {
1122             virtualRouter.add(vrouter);
1123         }
1124         return virtualRouter;
1125     }
1126
1127     public List<Action> getEgressAction(String interfaceName) {
1128         List<Action> actions = null;
1129         try {
1130             GetEgressActionsForInterfaceInputBuilder egressAction =
1131                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
1132             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1133                     interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
1134             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1135             if (!rpcResult.isSuccessful()) {
1136                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1137                         interfaceName, rpcResult.getErrors());
1138             } else {
1139                 actions = new ArrayList<Action>(rpcResult.getResult().nonnullAction().values());
1140             }
1141         } catch (InterruptedException | ExecutionException e) {
1142             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1143         }
1144         return actions;
1145     }
1146
1147     /**
1148      * Check ICMPv6 NS related match.
1149      *
1150      * @param dpnId DPN ID
1151      * @param lportTag VM Lport Tag
1152      * @param intf Virtual Interface
1153      * @param action ADD or DEL
1154      */
1155     protected void checkIcmpv6NsMatchAndResponderFlow(Uint64 dpnId, int lportTag, final IVirtualPort intf,
1156                                                       int action) {
1157         if (!ipv6ServiceEosHandler.isClusterOwner()) {
1158             LOG.trace("checkIcmpv6NsMatchAndResponderFlow: Not a cluster Owner, skip flow programming.");
1159             return;
1160         }
1161         VirtualNetwork vnet = getNetwork(intf.getNetworkID());
1162         Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = Collections.emptyList();
1163         if (vnet != null) {
1164             dpnIfaceList = vnet.getDpnIfaceList();
1165         }
1166         //VM interface case
1167
1168         if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_GATEWAY)
1169                 || intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.DEVICE_OWNER_DHCP)) {
1170             LOG.info("NA Responder Doesn't configure flows for type {}", intf.getDeviceOwner());
1171             return;
1172         }
1173         if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_INTERFACE)) {
1174             if (!dpnIfaceList.isEmpty()) {
1175                 LOG.debug("checkIcmpv6NsMatchAndResponderFlow: Router interface {} is {} for the network {}",
1176                         intf.getIntfUUID(), action == Ipv6ServiceConstants.ADD_FLOW ? "Added" : "Removed",
1177                         intf.getNetworkID().getValue());
1178                 final long networkElanTag = getNetworkElanTag(intf.getNetworkID());
1179                 //check if any VM is booted already before subnet is added to the router
1180                 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1181                     ndExecutorService.execute(() ->
1182                             checkVmBootBeforeSubnetAddRouter(dpnIfaceInfo, intf, networkElanTag, action));
1183                 }
1184                 /* Router interface case. Default NS punt flows per subnet
1185                  * 1. flow for matching nd_target = <Subnet_GW_IP>
1186                  * 2. flow for matching nd_target = <Subnet_GW_LLA>
1187                  */
1188                 for (Ipv6Address ndTarget : intf.getIpv6Addresses()) {
1189                     programIcmpv6NsDefaultPuntFlows(intf, ndTarget, action);
1190                 }
1191             }
1192         } else {
1193             if (dpnId.equals(Uint64.ZERO) || lportTag == 0) {
1194                 Interface interfaceState = ipv6ServiceUtils.getInterfaceStateFromOperDS(intf.getIntfUUID().getValue());
1195                 if (interfaceState == null) {
1196                     LOG.warn("checkIcmpv6NsMatchAndResponderFlow: Unable to retrieve interface state for port {}.",
1197                             intf.getIntfUUID().getValue());
1198                     return;
1199                 }
1200                 List<String> ofportIds = interfaceState.getLowerLayerIf();
1201                 NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
1202                 dpnId = Uint64.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
1203                 lportTag = interfaceState.getIfIndex();
1204             }
1205             LOG.debug("checkIcmpv6NsMatchAndResponderFlow: VM interface {} is {} on DPN {} for the network {}",
1206                     intf.getIntfUUID(), action == Ipv6ServiceConstants.ADD_FLOW ? "Added" : "Removed", dpnId,
1207                     intf.getNetworkID().getValue());
1208             VirtualPort routerPort = getRouterV6InterfaceForNetwork(intf.getNetworkID());
1209             if (routerPort != null) {
1210                 for (Ipv6Address ndTargetAddr : routerPort.getIpv6Addresses()) {
1211                     final Uint64 dpnIdFinal = dpnId;
1212                     final int lportTagFinal = lportTag;
1213                     coordinator.enqueueJob("NSResponder-" + lportTagFinal + ndTargetAddr.getValue(), () -> {
1214                         programIcmpv6NsMatchAndResponderFlow(dpnIdFinal, lportTagFinal,
1215                                 getNetworkElanTag(intf.getNetworkID()), intf,
1216                                 ndTargetAddr, routerPort.getMacAddress(),
1217                                 action);
1218                         return Collections.emptyList();
1219                     });
1220
1221                 }
1222                 if (!dpnIfaceList.isEmpty()) {
1223                     //check if VM is first or last VM on the DPN for the network
1224                     for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1225                         dpnIfaceInfo.setOvsNaResponderFlowConfiguredStatus(intf.getIntfUUID(), lportTag, action);
1226                         if ((dpnId.compareTo(dpnIfaceInfo.getDpId()) == 0)
1227                                 && (dpnIfaceInfo.ofPortMap.size() == Ipv6ServiceConstants.FIRST_OR_LAST_VM_ON_DPN)) {
1228                             LOG.debug("checkIcmpv6NsMatchAndResponderFlow: First or Last VM booted or removed on "
1229                                             + "DPN {} " + "for the network {} action {}", dpnId,
1230                                     intf.getNetworkID().getValue(), action);
1231                             for (Ipv6Address ndTarget : routerPort.getIpv6Addresses()) {
1232                            /* programIcmpv6NsDefaultPuntFlows(routerPort, ndTarget, action); */
1233                                 coordinator.enqueueJob("NSResponder-" + ndTarget.getValue(), () -> {
1234                                     programIcmp6NsDefaultPuntFlowsperDpn(routerPort, ndTarget, action, dpnIfaceInfo);
1235                                     return Collections.emptyList();
1236                                 });
1237                             }
1238                         } else {
1239                             continue;
1240                         }
1241                     }
1242                 }
1243             }
1244         }
1245     }
1246
1247     public void checkVmBootBeforeSubnetAddRouter(final VirtualNetwork.DpnInterfaceInfo dpnInterfaceInfo,
1248                                                  final IVirtualPort ivirtualPort, final long networkElanTag,
1249                                                  int action) {
1250         for (Uuid vmPort : dpnInterfaceInfo.ofPortMap.values()) {
1251             LOG.info("checkVmBootBeforeSubnetAddRouter: ofportMap VM port values {} and port name {}",
1252                     dpnInterfaceInfo.ofPortMap.values(), vmPort);
1253             Interface interfaceState = ipv6ServiceUtils.getInterfaceStateFromOperDS(vmPort.getValue());
1254             final int lportTag = interfaceState.getIfIndex();
1255             VirtualPort vmVirtualPort = getPort(vmPort);
1256             for (Ipv6Address ndTarget : ivirtualPort.getIpv6Addresses()) {
1257                 coordinator.enqueueJob("NSResponder-" + lportTag + ndTarget.getValue(), () -> {
1258                     programIcmpv6NsMatchAndResponderFlow(dpnInterfaceInfo.getDpId(),
1259                             lportTag, networkElanTag, vmVirtualPort, ndTarget,
1260                             ivirtualPort.getMacAddress(), action);
1261                     return Collections.emptyList();
1262                 });
1263             }
1264             dpnInterfaceInfo.setOvsNaResponderFlowConfiguredStatus(vmPort, lportTag, action);
1265         }
1266     }
1267
1268
1269     /**
1270      * Check ICMPv6 NS related match and action.
1271      *
1272      * @param dpnId DPN ID
1273      * @param lportTag VM LPort Tag
1274      * @param elanTag IPv6 Network Elan Tag
1275      * @param intf Virtual Interface
1276      * @param ndTargetAddr ND Target Address
1277      * @param rtrIntMacAddress Router Interface MAC Address
1278      * @param action ADD or DEL
1279      */
1280     private void programIcmpv6NsMatchAndResponderFlow(Uint64 dpnId, int lportTag, long elanTag, IVirtualPort intf,
1281                                                       Ipv6Address ndTargetAddr, String rtrIntMacAddress,
1282                                                       int action) {
1283
1284         LOG.trace("programIcmpv6NsMatchAndResponderFlow: Programming OVS based NA Responder Flow on DPN {} "
1285                         + "for network {}, port {} and IPv6Address {}.", dpnId, intf.getNetworkID().getValue(),
1286                 intf.getIntfUUID(), ndTargetAddr);
1287
1288         //NITHI
1289         //ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1290         ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION,
1291             tx -> {
1292                 /*Exact VM vNIC interface IPv6 and MAC address match flow */
1293                 ipv6ServiceUtils.instIcmpv6NsMatchFlow(NwConstants.IPV6_TABLE, dpnId,
1294                          elanTag, lportTag, intf.getMacAddress(), ndTargetAddr, action, tx, true);
1295                 /*Exact VM vNIC interface IPv6 address and missing ICMPv6 SLL option field match flow*/
1296                 ipv6ServiceUtils.instIcmpv6NsMatchFlow(NwConstants.IPV6_TABLE, dpnId,
1297                          elanTag, lportTag, intf.getMacAddress(), ndTargetAddr, action, tx, false);
1298                 /*table 81 NA responder flow with ICMPv6 TLL option field */
1299                 ipv6ServiceUtils.installIcmpv6NaResponderFlow(NwConstants.ARP_RESPONDER_TABLE,
1300                          dpnId, elanTag, lportTag, intf, ndTargetAddr, rtrIntMacAddress, action, tx, true);
1301                 /*table 81 NA responder flow without ICMPv6 TLL option field */
1302                 ipv6ServiceUtils.installIcmpv6NaResponderFlow(NwConstants.ARP_RESPONDER_TABLE,
1303                          dpnId, elanTag, lportTag, intf, ndTargetAddr, rtrIntMacAddress, action, tx, false);
1304             }), LOG, "Error" + (action == Ipv6ServiceConstants.ADD_FLOW ? "Installing" : "Uninstalling")
1305                 + " installing OVS based NA responder flows");
1306     }
1307
1308     public void  programIcmp6NsDefaultPuntFlowsperDpn(IVirtualPort routerPort, Ipv6Address ndTarget, int action,
1309                                                       VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo) {
1310         if (!ipv6ServiceEosHandler.isClusterOwner()) {
1311             LOG.trace("Not a cluster Owner, skip flow programming.");
1312             return;
1313         }
1314         Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
1315         ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION,
1316             tx -> {
1317                 ipv6ServiceUtils.installIcmpv6NsDefaultPuntFlow(NwConstants.IPV6_TABLE,
1318                      dpnIfaceInfo.getDpId(), elanTag, ndTarget,
1319                      action , tx);
1320                 dpnIfaceInfo.updateNDTargetAddress(ndTarget, action);
1321             }), LOG, "Error " + (action == Ipv6ServiceConstants.ADD_FLOW ? "Installing" : "Uninstalling")
1322                 + "OVS based NA responder default subnet punt flows");
1323     }
1324
1325     /**
1326      * NS unspecified use case.
1327      *
1328      * @param routerPort Neutron Router Virtual Port
1329      * @param ndTarget ND Target Address
1330      * @param action ADD or DEL
1331      */
1332     public void programIcmpv6NsDefaultPuntFlows(IVirtualPort routerPort, Ipv6Address ndTarget, int action) {
1333         if (!ipv6ServiceEosHandler.isClusterOwner()) {
1334             LOG.trace("Not a cluster Owner, skip flow programming.");
1335             return;
1336         }
1337         /*Long elanTag = getNetworkElanTag(routerPort.getNetworkID()); */
1338         VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
1339         if (vnet != null) {
1340             Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
1341             for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1342                 coordinator.enqueueJob("NSResponder-" + ndTarget.getValue(), () -> {
1343                     programIcmp6NsDefaultPuntFlowsperDpn(routerPort, ndTarget , action, dpnIfaceInfo);
1344                     return Collections.emptyList();
1345                 });
1346
1347             }
1348         }
1349     }
1350 }