2 * Copyright (c) 2015 Dell Inc. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.ipv6service;
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;
21 import java.util.Objects;
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.LoggingFutures;
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;
80 public class IfMgr implements ElementCache, AutoCloseable {
82 private static final Logger LOG = LoggerFactory.getLogger(IfMgr.class);
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;
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");
128 public void close() {
129 ndExecutorService.shutdown();
133 public ExecutorService getNDExecutorService() {
134 return ndExecutorService;
140 * @param rtrUuid router uuid
141 * @param rtrName router name
142 * @param tenantId tenant id
144 public void addRouter(Uuid rtrUuid, String rtrName, Uuid tenantId) {
146 VirtualRouter rtr = VirtualRouter.builder().routerUUID(rtrUuid).tenantID(tenantId).name(rtrName).build();
147 vrouters.put(rtrUuid, rtr);
149 Set<VirtualPort> intfList = unprocessedRouterIntfs.remove(rtrUuid);
150 if (intfList == null) {
151 LOG.debug("No unprocessed interfaces for the router {}", rtrUuid);
155 synchronized (intfList) {
156 for (VirtualPort intf : intfList) {
159 rtr.addInterface(intf);
161 for (VirtualSubnet snet : intf.getSubnets()) {
172 * @param rtrUuid router uuid
174 public void removeRouter(Uuid rtrUuid) {
176 VirtualRouter rtr = rtrUuid != null ? vrouters.remove(rtrUuid) : null;
179 removeUnprocessed(unprocessedRouterIntfs, rtrUuid);
181 LOG.error("Delete router failed for :{}", rtrUuid);
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.
197 public void addSubnet(Uuid snetId, String name, Uuid tenantId,
198 IpAddress gatewayIp, String ipVersion, IpPrefix subnetCidr,
199 String ipV6AddressMode, String ipV6RaMode) {
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);
209 VirtualSubnet snet = VirtualSubnet.builder().subnetUUID(snetId).tenantID(tenantId).name(name)
210 .gatewayIp(gatewayIp).subnetCidr(subnetCidr).ipVersion(ipVersion).ipv6AddressMode(ipV6AddressMode)
211 .ipv6RAMode(ipV6RaMode).build();
213 vsubnets.put(snetId, snet);
215 Set<VirtualPort> intfList = unprocessedSubnetIntfs.remove(snetId);
216 if (intfList == null) {
217 LOG.debug("No unprocessed interfaces for the subnet {}", snetId);
221 synchronized (intfList) {
222 for (VirtualPort intf : intfList) {
224 intf.setSubnet(snetId, snet);
225 snet.addInterface(intf);
227 VirtualRouter rtr = intf.getRouter();
231 updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
240 * @param snetId subnet id
242 public void removeSubnet(Uuid snetId) {
243 VirtualSubnet snet = snetId != null ? vsubnets.remove(snetId) : null;
245 LOG.info("removeSubnet is invoked for {}", snetId);
247 removeUnprocessed(unprocessedSubnetIntfs, snetId);
252 private VirtualRouter getRouter(Uuid rtrId) {
253 return rtrId != null ? vrouters.get(rtrId) : null;
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.
268 VirtualPort virInterface = vintfs.get(portId);
269 if (virInterface != null && Strings.isNullOrEmpty(virInterface.getDeviceOwner())) {
270 vintfs.remove(portId);
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);
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);
286 boolean newIntf = false;
287 VirtualPort prevIntf = vintfs.putIfAbsent(portId, intf);
288 if (prevIntf == null) {
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).
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);
302 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), llAddr, Ipv6ServiceConstants.ADD_FLOW);
304 programIcmpv6NaForwardFlows(intf.getNetworkID(), getSubnet(snetId), Ipv6ServiceConstants.ADD_FLOW);
307 intf.setSubnetInfo(snetId, fixedIp);
310 VirtualRouter rtr = getRouter(rtrId);
311 VirtualSubnet snet = getSubnet(snetId);
313 if (rtr != null && snet != null) {
315 intf.setSubnet(snetId, snet);
317 } else if (snet != null) {
318 intf.setSubnet(snetId, snet);
319 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
321 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
322 addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
325 if (networkId != null) {
326 LOG.trace("Adding vrouterv6IntfMap for network:{} intf:{}", networkId, intf);
327 vrouterv6IntfMap.put(networkId, intf);
330 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Controller)) {
331 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), fixedIp.getIpv6Address(),
332 Ipv6ServiceConstants.ADD_FLOW);
334 programIcmpv6NaPuntFlow(networkId, intf.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
337 LOG.debug("start the periodic RA Timer for routerIntf {}", portId);
338 transmitUnsolicitedRA(intf);
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);
346 LOG.info("Skip Router interface update for non-ipv6 port {}", portId);
349 Uuid networkID = intf.getNetworkID();
350 intf.clearSubnetInfo();
351 for (FixedIps fip : fixedIpsList) {
352 IpAddress fixedIp = fip.getIpAddress();
354 if (fixedIp.getIpv4Address() != null) {
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);
365 VirtualRouter rtr = getRouter(rtrId);
366 VirtualSubnet snet = getSubnet(subnetId);
368 if (rtr != null && snet != null) {
370 intf.setSubnet(subnetId, snet);
372 } else if (snet != null) {
373 intf.setSubnet(subnetId, snet);
374 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
376 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
377 addUnprocessed(unprocessedSubnetIntfs, subnetId, intf);
380 if (networkID != null) {
381 vrouterv6IntfMap.put(networkID, intf);
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.
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);
396 programIcmpv6NSPuntFlowForAddress(networkID, ips.getIpAddress().getIpv6Address(),
397 Ipv6ServiceConstants.DEL_FLOW);
403 private VirtualSubnet getSubnet(Uuid snetId) {
404 return snetId != null ? vsubnets.get(snetId) : null;
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);
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);
417 VirtualPort intf = VirtualPort.builder().intfUUID(portId).networkID(networkId).macAddress(macAddress)
418 .routerIntfFlag(false).deviceOwner(deviceOwner).build();
419 intf.setSubnetInfo(snetId, fixedIp);
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);
431 /* Update the intf dpnId/ofPort from the Operational Store */
432 updateInterfaceDpidOfPortInfo(portId);
434 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
435 checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, virtIntf, Ipv6ServiceConstants.ADD_FLOW);
437 return Collections.emptyList();
441 intf.setSubnetInfo(snetId, fixedIp);
444 VirtualSubnet snet = getSubnet(snetId);
447 intf.setSubnet(snetId, snet);
449 addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
454 private VirtualPort getPort(Uuid portId) {
455 return portId != null ? vintfs.get(portId) : null;
458 public void clearAnyExistingSubnetInfo(Uuid portId) {
459 VirtualPort intf = getPort(portId);
461 intf.clearSubnetInfo();
465 public void updateHostIntf(Uuid portId, Boolean portIncludesV6Address) {
466 VirtualPort intf = getPort(portId);
468 LOG.debug("Update Host interface failed. Could not get Host interface details {}", portId);
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
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);
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);
499 return Collections.emptyList();
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);
509 intf.setOfPort(ofPort);
511 // Update the network <--> List[dpnIds, List<ports>] cache.
512 VirtualNetwork vnet = getNetwork(intf.getNetworkID());
514 vnet.updateDpnPortInfo(dpId, ofPort, portId, Ipv6ServiceConstants.ADD_ENTRY);
516 LOG.error("In updateDpnInfo networks NOT FOUND: networkID {}, portId {}, dpId {}, ofPort {}",
517 intf.getNetworkID(), portId, dpId, ofPort);
518 addUnprocessed(unprocessedNetIntfs, intf.getNetworkID(), intf);
521 LOG.error("In updateDpnInfo port NOT FOUND: portId {}, dpId {}, ofPort {}",
522 portId, dpId, ofPort);
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);
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);
545 public void removePort(Uuid portId) {
546 VirtualPort intf = portId != null ? vintfs.remove(portId) : null;
549 Uuid networkID = intf.getNetworkID();
550 if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_INTERFACE)) {
551 LOG.info("In removePort for router interface, portId {}", portId);
553 if (networkID != null) {
554 vrouterv6IntfMap.remove(networkID, intf);
557 /* Router port is deleted. Remove the corresponding icmpv6 punt flows on all
558 the dpnIds which were hosting the VMs on the network.
560 programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.DEL_FLOW);
561 programIcmpv6NaPuntFlow(networkID, intf.getSubnets(), Ipv6ServiceConstants.DEL_FLOW);
563 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
564 checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
566 for (Ipv6Address ipv6Address : intf.getIpv6Addresses()) {
567 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address,
568 Ipv6ServiceConstants.DEL_FLOW);
572 for (VirtualSubnet subnet : intf.getSubnets()) {
573 programIcmpv6NaForwardFlows(networkID, subnet, Ipv6ServiceConstants.DEL_FLOW);
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);
584 Uint64 dpId = intf.getDpId();
585 vnet.updateDpnPortInfo(dpId, intf.getOfPort(), portId, Ipv6ServiceConstants.DEL_ENTRY);
587 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(),
588 Ipv6serviceConfig.NaResponderMode.Switch)) {
589 if (intf.getDpId() != null) {
590 checkIcmpv6NsMatchAndResponderFlow(intf.getDpId(), 0, intf, Ipv6ServiceConstants.DEL_FLOW);
592 checkIcmpv6NsMatchAndResponderFlow(Uint64.ZERO, 0, intf, Ipv6ServiceConstants.DEL_FLOW);
595 return Collections.emptyList();
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());
605 public void deleteInterface(Uuid interfaceUuid, String dpId) {
606 // Nothing to do for now
609 public void addUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id, VirtualPort intf) {
611 unprocessed.computeIfAbsent(id,
612 key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet())).add(intf);
617 public Set<VirtualPort> removeUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id) {
619 return unprocessed.remove(id);
624 public void addUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id, Integer action) {
625 unprocessed.put(id, action);
629 public Integer removeUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id) {
630 return unprocessed.remove(id);
633 private void addUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed, Uuid id, Ipv6Address ipv6Address,
635 if (action == Ipv6ServiceConstants.ADD_FLOW) {
636 unprocessed.computeIfAbsent(id, key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet()))
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);
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();
651 private void addUnprocessedNaFlows(Uuid networkId, Collection<VirtualSubnet> subnets, Integer action) {
652 if (action == Ipv6ServiceConstants.ADD_FLOW) {
653 unprocessedNetNaFlowIntfs.computeIfAbsent(networkId, key -> ConcurrentHashMap.newKeySet())
655 } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
656 Set<VirtualSubnet> subnetsInCache = unprocessedNetNaFlowIntfs.get(networkId);
657 if (subnetsInCache != null) {
658 subnetsInCache.removeAll(subnets);
663 private Set<VirtualSubnet> removeUnprocessedNaFlows(Uuid networkId) {
664 Set<VirtualSubnet> removedSubnets = unprocessedNetNaFlowIntfs.remove(networkId);
665 return removedSubnets != null ? removedSubnets : Collections.emptySet();
669 public VirtualPort getRouterV6InterfaceForNetwork(Uuid networkId) {
670 LOG.debug("obtaining the virtual interface for {}", networkId);
671 return networkId != null ? vrouterv6IntfMap.get(networkId) : null;
675 public VirtualPort obtainV6Interface(Uuid id) {
676 VirtualPort intf = getPort(id);
680 for (VirtualSubnet snet : intf.getSubnets()) {
681 if (snet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6)) {
688 private void programIcmpv6RSPuntFlows(Uuid networkId, int action) {
689 if (!ipv6ServiceEosHandler.isClusterOwner()) {
690 LOG.trace("Not a cluster Owner, skip flow programming.");
694 Long elanTag = getNetworkElanTag(networkId);
696 VirtualNetwork vnet = getNetwork(networkId);
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);
714 addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, action);
718 protected void programIcmpv6NSPuntFlowForAddress(Uuid networkId, Ipv6Address ipv6Address, int action) {
719 if (!ipv6ServiceEosHandler.isClusterOwner()) {
720 LOG.trace("Not a cluster Owner, skip flow programming.");
724 Long elanTag = getNetworkElanTag(networkId);
725 VirtualNetwork vnet = getNetwork(networkId);
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);
743 addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, action);
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.");
752 Long elanTag = getNetworkElanTag(networkID);
753 VirtualNetwork vnet = getNetwork(networkID);
755 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
756 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
757 if (dpnIfaceInfo.getDpId() == null) {
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);
778 addUnprocessedNaFlows(networkID, subnets, action);
782 public void handleInterfaceStateEvent(VirtualPort port, Uint64 dpId, VirtualPort routerPort,int lportTag ,
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);
790 if (Objects.equals(ipV6NAConfigHelper.getNaResponderMode(), Ipv6serviceConfig.NaResponderMode.Switch)) {
791 checkIcmpv6NsMatchAndResponderFlow(dpId, lportTag, port, addOrRemove);
794 if (elanTag != null) {
795 programIcmpv6NaForwardFlow(port, dpId, elanTag, addOrRemove);
797 addUnprocessedNaFlows(port.getNetworkID(), port.getSubnets(), addOrRemove);
801 private void programIcmpv6PuntFlows(IVirtualPort vmPort, Uint64 dpId, Long elanTag, VirtualPort routerPort,
803 Uuid networkId = vmPort.getNetworkID();
804 VirtualNetwork vnet = getNetwork(networkId);
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);
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);
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);
836 addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, Ipv6ServiceConstants.ADD_FLOW);
837 for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
838 addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
840 addUnprocessedNaFlows(networkId, routerPort.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
844 private void programIcmpv6NaForwardFlows(Uuid networkId, VirtualSubnet subnet, int action) {
845 if (!ipv6ServiceEosHandler.isClusterOwner()) {
846 LOG.trace("Not a cluster Owner, skip flow programming.");
849 if (subnet == null) {
850 LOG.debug("Subnet is null, skipping programIcmpv6NaForwardFlows.");
854 VirtualNetwork vnet = getNetwork(networkId);
856 if (!Ipv6ServiceUtils.isIpv6Subnet(subnet)) {
859 Long elanTag = getNetworkElanTag(networkId);
860 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
861 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
862 if (dpnIfaceInfo.getDpId() == null) {
865 List<VirtualPort> vmPorts = getVmPortsInSubnetByDpId(subnet.getSubnetUUID(), dpnIfaceInfo.getDpId());
866 for (VirtualPort vmPort : vmPorts) {
867 programIcmpv6NaForwardFlow(vmPort, dpnIfaceInfo.getDpId(), elanTag, action);
871 addUnprocessedNaFlows(networkId, Sets.newHashSet(subnet), action);
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.
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
884 private void programIcmpv6NaForwardFlow(IVirtualPort vmPort, Uint64 dpId, Long elanTag, int addOrRemove) {
885 ipv6ServiceUtils.installIcmpv6NaForwardFlow(NwConstants.IPV6_TABLE, vmPort, dpId, elanTag, addOrRemove);
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)) {
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);
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");
914 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
915 return interfaceName;
919 public Long updateNetworkElanTag(Uuid networkId) {
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);
927 net.setElanTag(elanTag);
934 public void updateNetworkMtuInfo(Uuid networkId, int mtu) {
935 VirtualNetwork net = getNetwork(networkId);
942 public VirtualNetwork getNetwork(Uuid networkId) {
943 return networkId != null ? vnetworks.get(networkId) : null;
947 public Long getNetworkElanTag(Uuid networkId) {
949 IVirtualNetwork net = getNetwork(networkId);
951 elanTag = net.getElanTag();
952 if (null == elanTag) {
953 elanTag = updateNetworkElanTag(networkId);
960 public int getNetworkMtu(Uuid networkId) {
962 IVirtualNetwork net = getNetwork(networkId);
969 public void addNetwork(Uuid networkId, int mtu) {
970 if (networkId == null) {
974 if (vnetworks.putIfAbsent(networkId, new VirtualNetwork(networkId)) == null) {
975 updateNetworkMtuInfo(networkId, mtu);
976 updateNetworkElanTag(networkId);
978 Set<VirtualPort> intfList = removeUnprocessed(unprocessedNetIntfs, networkId);
979 if (intfList == null) {
980 LOG.info("No unprocessed interfaces for the net {}", networkId);
984 for (VirtualPort intf : intfList) {
986 updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
990 Set<Ipv6Address> ipv6Addresses =
991 removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
993 for (Ipv6Address ipv6Address : ipv6Addresses) {
994 programIcmpv6NSPuntFlowForAddress(networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
997 Integer action = removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
998 programIcmpv6RSPuntFlows(networkId, action);
1000 Set<VirtualSubnet> subnets = removeUnprocessedNaFlows(networkId);
1001 programIcmpv6NaPuntFlow(networkId, subnets, Ipv6ServiceConstants.ADD_FLOW);
1002 for (VirtualSubnet subnet : subnets) {
1003 programIcmpv6NaForwardFlows(networkId, subnet, action);
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());
1017 removeUnprocessed(unprocessedNetIntfs, networkId);
1018 removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
1019 removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
1022 private void transmitRouterAdvertisement(VirtualPort intf, Ipv6RouterAdvertisementType advType) {
1023 Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt(packetService, this);
1025 VirtualNetwork vnet = getNetwork(intf.getNetworkID());
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
1033 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
1034 LOG.debug("transmitRouterAdvertisement: Transmitting RA {} for ELAN Tag {}",
1036 if (dpnIfaceInfo.getDpId() != null) {
1037 ipv6RouterAdvert.transmitRtrAdvertisement(advType, intf, elanTag, null,
1038 dpnIfaceInfo.getDpId(), intf.getIntfUUID(),
1039 ipV6NAConfigHelper.getIpv6RouterReachableTimeinMS());
1047 private void removeAllIcmpv6NSPuntFlowForNetwork(Uuid networkId) {
1048 Long elanTag = getNetworkElanTag(networkId);
1049 VirtualNetwork vnet = vnetworks.get(networkId);
1054 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
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);
1062 dpnIfaceInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.DEL_ENTRY);
1068 public void transmitUnsolicitedRA(Uuid portId) {
1069 VirtualPort port = getPort(portId);
1070 LOG.debug("in transmitUnsolicitedRA for {}, port {}", portId, port);
1072 transmitUnsolicitedRA(port);
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.
1081 transmitRouterAdvertisement(port, Ipv6RouterAdvertisementType.UNSOLICITED_ADVERTISEMENT);
1083 Timeout portTimeout = timer.setPeriodicTransmissionTimeout(port.getPeriodicTimer(),
1084 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL,
1086 port.setPeriodicTimeout(portTimeout);
1087 LOG.debug("re-started periodic RA Timer for routerIntf {}, int {}s", port.getIntfUUID(),
1088 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL);
1092 public List<IVirtualPort> getInterfaceCache() {
1093 List<IVirtualPort> virtualPorts = new ArrayList<>();
1094 for (VirtualPort vport: vintfs.values()) {
1095 virtualPorts.add(vport);
1097 return virtualPorts;
1101 public List<IVirtualNetwork> getNetworkCache() {
1102 List<IVirtualNetwork> virtualNetworks = new ArrayList<>();
1103 for (VirtualNetwork vnet: vnetworks.values()) {
1104 virtualNetworks.add(vnet);
1106 return virtualNetworks;
1110 public List<IVirtualSubnet> getSubnetCache() {
1111 List<IVirtualSubnet> virtualSubnets = new ArrayList<>();
1112 for (VirtualSubnet vsubnet: vsubnets.values()) {
1113 virtualSubnets.add(vsubnet);
1115 return virtualSubnets;
1119 public List<IVirtualRouter> getRouterCache() {
1120 List<IVirtualRouter> virtualRouter = new ArrayList<>();
1121 for (VirtualRouter vrouter: vrouters.values()) {
1122 virtualRouter.add(vrouter);
1124 return virtualRouter;
1127 public List<Action> getEgressAction(String interfaceName) {
1128 List<Action> actions = null;
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());
1139 actions = new ArrayList<Action>(rpcResult.getResult().nonnullAction().values());
1141 } catch (InterruptedException | ExecutionException e) {
1142 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
1148 * Check ICMPv6 NS related match.
1150 * @param dpnId DPN ID
1151 * @param lportTag VM Lport Tag
1152 * @param intf Virtual Interface
1153 * @param action ADD or DEL
1155 protected void checkIcmpv6NsMatchAndResponderFlow(Uint64 dpnId, int lportTag, final IVirtualPort intf,
1157 if (!ipv6ServiceEosHandler.isClusterOwner()) {
1158 LOG.trace("checkIcmpv6NsMatchAndResponderFlow: Not a cluster Owner, skip flow programming.");
1161 VirtualNetwork vnet = getNetwork(intf.getNetworkID());
1162 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = Collections.emptyList();
1164 dpnIfaceList = vnet.getDpnIfaceList();
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());
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));
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>
1188 for (Ipv6Address ndTarget : intf.getIpv6Addresses()) {
1189 programIcmpv6NsDefaultPuntFlows(intf, ndTarget, action);
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());
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();
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(),
1218 return Collections.emptyList();
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();
1247 public void checkVmBootBeforeSubnetAddRouter(final VirtualNetwork.DpnInterfaceInfo dpnInterfaceInfo,
1248 final IVirtualPort ivirtualPort, final long networkElanTag,
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();
1264 dpnInterfaceInfo.setOvsNaResponderFlowConfiguredStatus(vmPort, lportTag, action);
1270 * Check ICMPv6 NS related match and action.
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
1280 private void programIcmpv6NsMatchAndResponderFlow(Uint64 dpnId, int lportTag, long elanTag, IVirtualPort intf,
1281 Ipv6Address ndTargetAddr, String rtrIntMacAddress,
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);
1289 //LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
1290 LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION,
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");
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.");
1314 Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
1315 LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(Datastore.CONFIGURATION,
1317 ipv6ServiceUtils.installIcmpv6NsDefaultPuntFlow(NwConstants.IPV6_TABLE,
1318 dpnIfaceInfo.getDpId(), elanTag, ndTarget,
1320 dpnIfaceInfo.updateNDTargetAddress(ndTarget, action);
1321 }), LOG, "Error " + (action == Ipv6ServiceConstants.ADD_FLOW ? "Installing" : "Uninstalling")
1322 + "OVS based NA responder default subnet punt flows");
1326 * NS unspecified use case.
1328 * @param routerPort Neutron Router Virtual Port
1329 * @param ndTarget ND Target Address
1330 * @param action ADD or DEL
1332 public void programIcmpv6NsDefaultPuntFlows(IVirtualPort routerPort, Ipv6Address ndTarget, int action) {
1333 if (!ipv6ServiceEosHandler.isClusterOwner()) {
1334 LOG.trace("Not a cluster Owner, skip flow programming.");
1337 /*Long elanTag = getNetworkElanTag(routerPort.getNetworkID()); */
1338 VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
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();