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;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.Future;
26 import java.util.concurrent.TimeUnit;
27 import javax.annotation.PreDestroy;
28 import javax.inject.Inject;
29 import javax.inject.Singleton;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.opendaylight.genius.ipv6util.api.Ipv6Constants.Ipv6RouterAdvertisementType;
32 import org.opendaylight.genius.ipv6util.api.Ipv6Util;
33 import org.opendaylight.genius.mdsalutil.MDSALUtil;
34 import org.opendaylight.genius.mdsalutil.NwConstants;
35 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
36 import org.opendaylight.mdsal.binding.api.DataBroker;
37 import org.opendaylight.netvirt.elanmanager.api.IElanService;
38 import org.opendaylight.netvirt.ipv6service.api.ElementCache;
39 import org.opendaylight.netvirt.ipv6service.api.IVirtualNetwork;
40 import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
41 import org.opendaylight.netvirt.ipv6service.api.IVirtualRouter;
42 import org.opendaylight.netvirt.ipv6service.api.IVirtualSubnet;
43 import org.opendaylight.netvirt.ipv6service.utils.Ipv6PeriodicTrQueue;
44 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceConstants;
45 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
46 import org.opendaylight.netvirt.ipv6service.utils.Ipv6TimerWheel;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetEgressActionsForInterfaceOutput;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexInputBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetInterfaceFromIfIndexOutput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.instances.ElanInstance;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
65 import org.opendaylight.yangtools.yang.common.RpcResult;
66 import org.opendaylight.yangtools.yang.common.Uint64;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
71 public class IfMgr implements ElementCache, AutoCloseable {
73 private static final Logger LOG = LoggerFactory.getLogger(IfMgr.class);
75 private final ConcurrentMap<Uuid, VirtualRouter> vrouters = new ConcurrentHashMap<>();
76 private final ConcurrentMap<Uuid, VirtualNetwork> vnetworks = new ConcurrentHashMap<>();
77 private final ConcurrentMap<Uuid, VirtualSubnet> vsubnets = new ConcurrentHashMap<>();
78 private final ConcurrentMap<Uuid, VirtualPort> vintfs = new ConcurrentHashMap<>();
79 private final ConcurrentMap<Uuid, VirtualPort> vrouterv6IntfMap = new ConcurrentHashMap<>();
80 private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedRouterIntfs = new ConcurrentHashMap<>();
81 private final ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedSubnetIntfs = new ConcurrentHashMap<>();
82 private static ConcurrentMap<Uuid, Set<VirtualPort>> unprocessedNetIntfs = new ConcurrentHashMap<>();
83 private static ConcurrentMap<Uuid, Integer> unprocessedNetRSFlowIntfs = new ConcurrentHashMap<>();
84 private static ConcurrentMap<Uuid, Set<Ipv6Address>> unprocessedNetNSFlowIntfs = new ConcurrentHashMap<>();
85 private static ConcurrentMap<Uuid, Set<VirtualSubnet>> unprocessedNetNaFlowIntfs = new ConcurrentHashMap<>();
86 private final OdlInterfaceRpcService interfaceManagerRpc;
87 private final IElanService elanProvider;
88 private final Ipv6ServiceUtils ipv6ServiceUtils;
89 private final DataBroker dataBroker;
90 private final Ipv6ServiceEosHandler ipv6ServiceEosHandler;
91 private final PacketProcessingService packetService;
92 private final Ipv6PeriodicTrQueue ipv6Queue = new Ipv6PeriodicTrQueue(this::transmitUnsolicitedRA);
93 private final Ipv6TimerWheel timer = new Ipv6TimerWheel();
94 private final JobCoordinator jobCoordinator;
97 public IfMgr(DataBroker dataBroker, IElanService elanProvider, OdlInterfaceRpcService interfaceManagerRpc,
98 PacketProcessingService packetService, Ipv6ServiceUtils ipv6ServiceUtils,
99 Ipv6ServiceEosHandler ipv6ServiceEosHandler, JobCoordinator jobCoordinator) {
100 this.dataBroker = dataBroker;
101 this.elanProvider = elanProvider;
102 this.interfaceManagerRpc = interfaceManagerRpc;
103 this.packetService = packetService;
104 this.ipv6ServiceUtils = ipv6ServiceUtils;
105 this.ipv6ServiceEosHandler = ipv6ServiceEosHandler;
106 this.jobCoordinator = jobCoordinator;
107 LOG.info("IfMgr is enabled");
112 public void close() {
119 * @param rtrUuid router uuid
120 * @param rtrName router name
121 * @param tenantId tenant id
123 public void addRouter(Uuid rtrUuid, String rtrName, Uuid tenantId) {
125 VirtualRouter rtr = VirtualRouter.builder().routerUUID(rtrUuid).tenantID(tenantId).name(rtrName).build();
126 vrouters.put(rtrUuid, rtr);
128 Set<VirtualPort> intfList = unprocessedRouterIntfs.remove(rtrUuid);
129 if (intfList == null) {
130 LOG.debug("No unprocessed interfaces for the router {}", rtrUuid);
134 synchronized (intfList) {
135 for (VirtualPort intf : intfList) {
138 rtr.addInterface(intf);
140 for (VirtualSubnet snet : intf.getSubnets()) {
151 * @param rtrUuid router uuid
153 public void removeRouter(Uuid rtrUuid) {
155 VirtualRouter rtr = rtrUuid != null ? vrouters.remove(rtrUuid) : null;
158 removeUnprocessed(unprocessedRouterIntfs, rtrUuid);
160 LOG.error("Delete router failed for :{}", rtrUuid);
167 * @param snetId subnet id
168 * @param name subnet name
169 * @param tenantId tenant id
170 * @param gatewayIp gateway ip address
171 * @param ipVersion IP Version "IPv4 or IPv6"
172 * @param subnetCidr subnet CIDR
173 * @param ipV6AddressMode Address Mode of IPv6 Subnet
174 * @param ipV6RaMode RA Mode of IPv6 Subnet.
176 public void addSubnet(Uuid snetId, String name, Uuid tenantId,
177 IpAddress gatewayIp, String ipVersion, IpPrefix subnetCidr,
178 String ipV6AddressMode, String ipV6RaMode) {
180 // Save the gateway ipv6 address in its fully expanded format. We always store the v6Addresses
181 // in expanded form and are used during Neighbor Discovery Support.
182 if (gatewayIp != null) {
183 Ipv6Address addr = new Ipv6Address(InetAddresses
184 .forString(gatewayIp.getIpv6Address().getValue()).getHostAddress());
185 gatewayIp = new IpAddress(addr);
188 VirtualSubnet snet = VirtualSubnet.builder().subnetUUID(snetId).tenantID(tenantId).name(name)
189 .gatewayIp(gatewayIp).subnetCidr(subnetCidr).ipVersion(ipVersion).ipv6AddressMode(ipV6AddressMode)
190 .ipv6RAMode(ipV6RaMode).build();
192 vsubnets.put(snetId, snet);
194 Set<VirtualPort> intfList = unprocessedSubnetIntfs.remove(snetId);
195 if (intfList == null) {
196 LOG.debug("No unprocessed interfaces for the subnet {}", snetId);
200 synchronized (intfList) {
201 for (VirtualPort intf : intfList) {
203 intf.setSubnet(snetId, snet);
204 snet.addInterface(intf);
206 VirtualRouter rtr = intf.getRouter();
210 updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
219 * @param snetId subnet id
221 public void removeSubnet(Uuid snetId) {
222 VirtualSubnet snet = snetId != null ? vsubnets.remove(snetId) : null;
224 LOG.info("removeSubnet is invoked for {}", snetId);
226 removeUnprocessed(unprocessedSubnetIntfs, snetId);
231 private VirtualRouter getRouter(Uuid rtrId) {
232 return rtrId != null ? vrouters.get(rtrId) : null;
235 public void addRouterIntf(Uuid portId, Uuid rtrId, Uuid snetId,
236 Uuid networkId, IpAddress fixedIp, String macAddress,
237 String deviceOwner) {
238 LOG.debug("addRouterIntf portId {}, rtrId {}, snetId {}, networkId {}, ip {}, mac {}",
239 portId, rtrId, snetId, networkId, fixedIp, macAddress);
240 /* Added the below logic for supporting neutron router interface creation.
241 * Since when neutron port is created with fixed Ipv6 address, that time it will be
242 * treated as a host port and it will be added into the vintfs map through
243 * NeutronPortChangeListener ADD() event.
244 * Later the same neutron port is added to router this time it will be treated as
245 * a router_interface through NeutronPortChangeListener UPDATE() event.
247 VirtualPort virInterface = vintfs.get(portId);
248 if (virInterface != null && Strings.isNullOrEmpty(virInterface.getDeviceOwner())) {
249 vintfs.remove(portId);
251 //Save the interface ipv6 address in its fully expanded format
252 Ipv6Address addr = new Ipv6Address(InetAddresses
253 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
254 fixedIp = new IpAddress(addr);
256 VirtualPort intf = VirtualPort.builder().intfUUID(portId).networkID(networkId).macAddress(macAddress)
257 .routerIntfFlag(true).deviceOwner(deviceOwner).build();
258 intf.setSubnetInfo(snetId, fixedIp);
259 intf.setPeriodicTimer(ipv6Queue);
260 int networkMtu = getNetworkMtu(networkId);
261 if (networkMtu != 0) {
262 intf.setMtu(networkMtu);
265 boolean newIntf = false;
266 VirtualPort prevIntf = vintfs.putIfAbsent(portId, intf);
267 if (prevIntf == null) {
269 MacAddress ifaceMac = MacAddress.getDefaultInstance(macAddress);
270 Ipv6Address llAddr = Ipv6Util.getIpv6LinkLocalAddressFromMac(ifaceMac);
271 /* A new router interface is created. This is basically triggered when an
272 IPv6 subnet is associated to the router. Check if network is already hosting
273 any VMs. If so, on all the hosts that have VMs on the network, program the
274 icmpv6 punt flows in IPV6_TABLE(45).
276 programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.ADD_FLOW);
277 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), llAddr, Ipv6ServiceConstants.ADD_FLOW);
278 programIcmpv6NaForwardFlows(intf.getNetworkID(), getSubnet(snetId), Ipv6ServiceConstants.ADD_FLOW);
281 intf.setSubnetInfo(snetId, fixedIp);
284 VirtualRouter rtr = getRouter(rtrId);
285 VirtualSubnet snet = getSubnet(snetId);
287 if (rtr != null && snet != null) {
289 intf.setSubnet(snetId, snet);
291 } else if (snet != null) {
292 intf.setSubnet(snetId, snet);
293 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
295 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
296 addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
299 if (networkId != null) {
300 vrouterv6IntfMap.put(networkId, intf);
303 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), fixedIp.getIpv6Address(), Ipv6ServiceConstants.ADD_FLOW);
304 programIcmpv6NaPuntFlow(networkId, intf.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
307 LOG.debug("start the periodic RA Timer for routerIntf {}", portId);
308 transmitUnsolicitedRA(intf);
312 public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList, Set<FixedIps> deletedIps) {
313 LOG.info("updateRouterIntf portId {}, fixedIpsList {} ", portId, fixedIpsList);
314 VirtualPort intf = getPort(portId);
316 LOG.info("Skip Router interface update for non-ipv6 port {}", portId);
319 Uuid networkID = intf.getNetworkID();
320 intf.clearSubnetInfo();
321 for (FixedIps fip : fixedIpsList) {
322 IpAddress fixedIp = fip.getIpAddress();
324 if (fixedIp.getIpv4Address() != null) {
328 //Save the interface ipv6 address in its fully expanded format
329 Ipv6Address addr = new Ipv6Address(InetAddresses
330 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
331 fixedIp = new IpAddress(addr);
332 Uuid subnetId = fip.getSubnetId();
333 intf.setSubnetInfo(subnetId, fixedIp);
335 VirtualRouter rtr = getRouter(rtrId);
336 VirtualSubnet snet = getSubnet(subnetId);
338 if (rtr != null && snet != null) {
340 intf.setSubnet(subnetId, snet);
342 } else if (snet != null) {
343 intf.setSubnet(subnetId, snet);
344 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
346 addUnprocessed(unprocessedRouterIntfs, rtrId, intf);
347 addUnprocessed(unprocessedSubnetIntfs, subnetId, intf);
350 if (networkID != null) {
351 vrouterv6IntfMap.put(networkID, intf);
356 * This is a port update event for routerPort. Check if any IPv6 subnet is added or removed from the
357 * router port. Depending on subnet added/removed, we add/remove the corresponding flows from
358 * IPV6_TABLE(45). Add is handled in addInterfaceInfo(), delete case is handled here.
360 for (FixedIps ips : deletedIps) {
361 VirtualSubnet snet = getSubnet(ips.getSubnetId());
362 programIcmpv6NaPuntFlow(networkID, Lists.newArrayList(snet), Ipv6ServiceConstants.DEL_FLOW);
363 programIcmpv6NSPuntFlowForAddress(networkID, ips.getIpAddress().getIpv6Address(),
364 Ipv6ServiceConstants.DEL_FLOW);
369 private VirtualSubnet getSubnet(Uuid snetId) {
370 return snetId != null ? vsubnets.get(snetId) : null;
373 public void addHostIntf(Uuid portId, Uuid snetId, Uuid networkId,
374 IpAddress fixedIp, String macAddress, String deviceOwner) {
375 LOG.debug("addHostIntf portId {}, snetId {}, networkId {}, ip {}, mac {}",
376 portId, snetId, networkId, fixedIp, macAddress);
378 //Save the interface ipv6 address in its fully expanded format
379 Ipv6Address addr = new Ipv6Address(InetAddresses
380 .forString(fixedIp.getIpv6Address().getValue()).getHostAddress());
381 fixedIp = new IpAddress(addr);
383 VirtualPort intf = VirtualPort.builder().intfUUID(portId).networkID(networkId).macAddress(macAddress)
384 .routerIntfFlag(false).deviceOwner(deviceOwner).build();
385 intf.setSubnetInfo(snetId, fixedIp);
387 VirtualPort prevIntf = vintfs.putIfAbsent(portId, intf);
388 if (prevIntf == null) {
389 Long elanTag = getNetworkElanTag(networkId);
390 VirtualPort virtIntf = intf;
391 jobCoordinator.enqueueJob("IPv6-" + String.valueOf(portId), () -> {
392 // Do service binding for the port and set the serviceBindingStatus to true.
393 ipv6ServiceUtils.bindIpv6Service(portId.getValue(), elanTag, NwConstants.IPV6_TABLE);
394 virtIntf.setServiceBindingStatus(true);
396 /* Update the intf dpnId/ofPort from the Operational Store */
397 updateInterfaceDpidOfPortInfo(portId);
398 return Collections.emptyList();
403 intf.setSubnetInfo(snetId, fixedIp);
406 VirtualSubnet snet = getSubnet(snetId);
409 intf.setSubnet(snetId, snet);
411 addUnprocessed(unprocessedSubnetIntfs, snetId, intf);
416 private VirtualPort getPort(Uuid portId) {
417 return portId != null ? vintfs.get(portId) : null;
420 public void clearAnyExistingSubnetInfo(Uuid portId) {
421 VirtualPort intf = getPort(portId);
423 intf.clearSubnetInfo();
427 public void updateHostIntf(Uuid portId, Boolean portIncludesV6Address) {
428 VirtualPort intf = getPort(portId);
430 LOG.debug("Update Host interface failed. Could not get Host interface details {}", portId);
434 /* If the VMPort initially included an IPv6 address (along with IPv4 address) and IPv6 address
435 was removed, we will have to unbind the service on the VM port. Similarly we do a ServiceBind
438 if (portIncludesV6Address) {
439 if (!intf.getServiceBindingStatus()) {
440 Long elanTag = getNetworkElanTag(intf.getNetworkID());
441 LOG.info("In updateHostIntf, service binding for portId {}", portId);
442 jobCoordinator.enqueueJob("IPv6-" + String.valueOf(portId), () -> {
443 ipv6ServiceUtils.bindIpv6Service(portId.getValue(), elanTag, NwConstants.IPV6_TABLE);
444 intf.setServiceBindingStatus(true);
445 return Collections.emptyList();
449 LOG.info("In updateHostIntf, removing service binding for portId {}", portId);
450 jobCoordinator.enqueueJob("IPv6-" + String.valueOf(portId), () -> {
451 ipv6ServiceUtils.unbindIpv6Service(portId.getValue());
452 intf.setServiceBindingStatus(false);
453 return Collections.emptyList();
458 public void updateDpnInfo(Uuid portId, Uint64 dpId, Long ofPort) {
459 LOG.info("In updateDpnInfo portId {}, dpId {}, ofPort {}",
460 portId, dpId, ofPort);
461 VirtualPort intf = getPort(portId);
464 intf.setOfPort(ofPort);
466 // Update the network <--> List[dpnIds, List<ports>] cache.
467 VirtualNetwork vnet = getNetwork(intf.getNetworkID());
469 vnet.updateDpnPortInfo(dpId, ofPort, Ipv6ServiceConstants.ADD_ENTRY);
471 LOG.error("In updateDpnInfo networks NOT FOUND: networkID {}, portId {}, dpId {}, ofPort {}",
472 intf.getNetworkID(), portId, dpId, ofPort);
473 addUnprocessed(unprocessedNetIntfs, intf.getNetworkID(), intf);
476 LOG.error("In updateDpnInfo port NOT FOUND: portId {}, dpId {}, ofPort {}",
477 portId, dpId, ofPort);
482 public void updateInterfaceDpidOfPortInfo(Uuid portId) {
483 LOG.debug("In updateInterfaceDpidOfPortInfo portId {}", portId);
484 Interface interfaceState = ipv6ServiceUtils.getInterfaceStateFromOperDS(portId.getValue());
485 if (interfaceState == null) {
486 LOG.warn("In updateInterfaceDpidOfPortInfo, port info not found in Operational Store {}.", portId);
490 List<String> ofportIds = interfaceState.getLowerLayerIf();
491 NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
492 Uint64 dpId = ipv6ServiceUtils.getDpIdFromInterfaceState(interfaceState);
493 if (!dpId.equals(Ipv6ServiceConstants.INVALID_DPID)) {
494 Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
495 updateDpnInfo(portId, dpId, ofPort);
500 public void removePort(Uuid portId) {
501 VirtualPort intf = portId != null ? vintfs.remove(portId) : null;
504 Uuid networkID = intf.getNetworkID();
505 if (intf.getDeviceOwner().equalsIgnoreCase(Ipv6ServiceConstants.NETWORK_ROUTER_INTERFACE)) {
506 LOG.info("In removePort for router interface, portId {}", portId);
508 if (networkID != null) {
509 vrouterv6IntfMap.remove(networkID, intf);
512 /* Router port is deleted. Remove the corresponding icmpv6 punt flows on all
513 the dpnIds which were hosting the VMs on the network.
515 programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6ServiceConstants.DEL_FLOW);
516 programIcmpv6NaPuntFlow(networkID, intf.getSubnets(), Ipv6ServiceConstants.DEL_FLOW);
517 for (Ipv6Address ipv6Address: intf.getIpv6Addresses()) {
518 programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
520 for (VirtualSubnet subnet : intf.getSubnets()) {
521 programIcmpv6NaForwardFlows(networkID, subnet, Ipv6ServiceConstants.DEL_FLOW);
523 transmitRouterAdvertisement(intf, Ipv6RouterAdvertisementType.CEASE_ADVERTISEMENT);
524 timer.cancelPeriodicTransmissionTimeout(intf.getPeriodicTimeout());
525 intf.resetPeriodicTimeout();
526 LOG.debug("Reset the periodic RA Timer for intf {}", intf.getIntfUUID());
528 LOG.info("In removePort for host interface, portId {}", portId);
529 jobCoordinator.enqueueJob("IPv6-" + String.valueOf(portId), () -> {
530 // Remove the serviceBinding entry for the port.
531 ipv6ServiceUtils.unbindIpv6Service(portId.getValue());
532 // Remove the portId from the (network <--> List[dpnIds, List <ports>]) cache.
533 VirtualNetwork vnet = getNetwork(networkID);
535 Uint64 dpId = intf.getDpId();
536 vnet.updateDpnPortInfo(dpId, intf.getOfPort(), Ipv6ServiceConstants.DEL_ENTRY);
538 return Collections.emptyList();
544 public void deleteInterface(Uuid interfaceUuid, String dpId) {
545 // Nothing to do for now
548 public void addUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id, VirtualPort intf) {
550 unprocessed.computeIfAbsent(id,
551 key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet())).add(intf);
556 public Set<VirtualPort> removeUnprocessed(Map<Uuid, Set<VirtualPort>> unprocessed, Uuid id) {
558 return unprocessed.remove(id);
563 public void addUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id, Integer action) {
564 unprocessed.put(id, action);
568 public Integer removeUnprocessedRSFlows(Map<Uuid, Integer> unprocessed, Uuid id) {
569 return unprocessed.remove(id);
572 private void addUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed, Uuid id, Ipv6Address ipv6Address,
574 if (action == Ipv6ServiceConstants.ADD_FLOW) {
575 unprocessed.computeIfAbsent(id, key -> Collections.synchronizedSet(ConcurrentHashMap.newKeySet()))
577 } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
578 Set<Ipv6Address> ipv6AddressesList = unprocessed.get(id);
579 if ((ipv6AddressesList != null) && (ipv6AddressesList.contains(ipv6Address))) {
580 ipv6AddressesList.remove(ipv6Address);
585 private Set<Ipv6Address> removeUnprocessedNSFlows(Map<Uuid, Set<Ipv6Address>> unprocessed, Uuid id) {
586 Set<Ipv6Address> removedIps = unprocessed.remove(id);
587 return removedIps != null ? removedIps : Collections.emptySet();
590 private void addUnprocessedNaFlows(Uuid networkId, Collection<VirtualSubnet> subnets, Integer action) {
591 if (action == Ipv6ServiceConstants.ADD_FLOW) {
592 unprocessedNetNaFlowIntfs.computeIfAbsent(networkId, key -> ConcurrentHashMap.newKeySet())
594 } else if (action == Ipv6ServiceConstants.DEL_FLOW) {
595 Set<VirtualSubnet> subnetsInCache = unprocessedNetNaFlowIntfs.get(networkId);
596 if (subnetsInCache != null) {
597 subnetsInCache.removeAll(subnets);
602 private Set<VirtualSubnet> removeUnprocessedNaFlows(Uuid networkId) {
603 Set<VirtualSubnet> removedSubnets = unprocessedNetNaFlowIntfs.remove(networkId);
604 return removedSubnets != null ? removedSubnets : Collections.emptySet();
608 public VirtualPort getRouterV6InterfaceForNetwork(Uuid networkId) {
609 LOG.debug("obtaining the virtual interface for {}", networkId);
610 return networkId != null ? vrouterv6IntfMap.get(networkId) : null;
614 public VirtualPort obtainV6Interface(Uuid id) {
615 VirtualPort intf = getPort(id);
619 for (VirtualSubnet snet : intf.getSubnets()) {
620 if (snet.getIpVersion().equals(Ipv6ServiceConstants.IP_VERSION_V6)) {
627 private void programIcmpv6RSPuntFlows(Uuid networkId, int action) {
628 if (!ipv6ServiceEosHandler.isClusterOwner()) {
629 LOG.trace("Not a cluster Owner, skip flow programming.");
633 Long elanTag = getNetworkElanTag(networkId);
635 VirtualNetwork vnet = getNetwork(networkId);
637 List<Uint64> dpnList = vnet.getDpnsHostingNetwork();
638 for (Uint64 dpId : dpnList) {
639 flowStatus = vnet.getRSPuntFlowStatusOnDpnId(dpId);
640 if (action == Ipv6ServiceConstants.ADD_FLOW
641 && flowStatus == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
642 ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
643 Ipv6ServiceConstants.ADD_FLOW);
644 vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
645 } else if (action == Ipv6ServiceConstants.DEL_FLOW
646 && flowStatus == Ipv6ServiceConstants.FLOWS_CONFIGURED) {
647 ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
648 Ipv6ServiceConstants.DEL_FLOW);
649 vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED);
653 addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, action);
657 private void programIcmpv6NSPuntFlowForAddress(Uuid networkId, Ipv6Address ipv6Address, int action) {
658 if (!ipv6ServiceEosHandler.isClusterOwner()) {
659 LOG.trace("Not a cluster Owner, skip flow programming.");
663 Long elanTag = getNetworkElanTag(networkId);
664 VirtualNetwork vnet = getNetwork(networkId);
666 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
667 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
668 if (action == Ipv6ServiceConstants.ADD_FLOW
669 && !dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)
670 && dpnIfaceInfo.getDpId() != null) {
671 ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
672 ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
673 dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
674 } else if (action == Ipv6ServiceConstants.DEL_FLOW
675 && dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
676 ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
677 ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
678 dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
682 addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, action);
686 private void programIcmpv6NaPuntFlow(Uuid networkID, Collection<VirtualSubnet> subnets, int action) {
687 if (!ipv6ServiceEosHandler.isClusterOwner()) {
688 LOG.trace("Not a cluster Owner, skip flow programming.");
691 Long elanTag = getNetworkElanTag(networkID);
692 VirtualNetwork vnet = getNetwork(networkID);
694 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
695 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
696 if (dpnIfaceInfo.getDpId() == null) {
699 for (VirtualSubnet subnet : subnets) {
700 Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
701 if (ipv6SubnetPrefix != null) {
702 if (action == Ipv6ServiceConstants.ADD_FLOW
703 && !dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
704 ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
705 dpnIfaceInfo.getDpId(), elanTag, action);
706 dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
707 } else if (action == Ipv6ServiceConstants.DEL_FLOW
708 && dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
709 ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
710 dpnIfaceInfo.getDpId(), elanTag, action);
711 dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
717 addUnprocessedNaFlows(networkID, subnets, action);
721 public void handleInterfaceStateEvent(VirtualPort port, Uint64 dpId, VirtualPort routerPort, int addOrRemove) {
722 Long elanTag = getNetworkElanTag(port.getNetworkID());
723 if (addOrRemove == Ipv6ServiceConstants.ADD_FLOW && routerPort != null) {
724 // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
725 programIcmpv6PuntFlows(port, dpId, elanTag, routerPort);
727 if (elanTag != null) {
728 programIcmpv6NaForwardFlow(port, dpId, elanTag, addOrRemove);
730 addUnprocessedNaFlows(port.getNetworkID(), port.getSubnets(), addOrRemove);
734 private void programIcmpv6PuntFlows(IVirtualPort vmPort, Uint64 dpId, Long elanTag, VirtualPort routerPort) {
735 Uuid networkId = vmPort.getNetworkID();
736 VirtualNetwork vnet = getNetwork(networkId);
738 VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
739 if (null != dpnInfo) {
740 if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6ServiceConstants.FLOWS_NOT_CONFIGURED) {
741 ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
742 Ipv6ServiceConstants.ADD_FLOW);
743 vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6ServiceConstants.FLOWS_CONFIGURED);
746 for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
747 if (!dpnInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
748 ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
749 elanTag, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
750 dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
754 for (VirtualSubnet subnet : routerPort.getSubnets()) {
755 Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
756 if (ipv6SubnetPrefix != null
757 && !dpnInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
758 ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix, dpId,
759 elanTag, Ipv6ServiceConstants.ADD_FLOW);
760 dpnInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), Ipv6ServiceConstants.ADD_FLOW);
765 addUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId, Ipv6ServiceConstants.ADD_FLOW);
766 for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
767 addUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
769 addUnprocessedNaFlows(networkId, routerPort.getSubnets(), Ipv6ServiceConstants.ADD_FLOW);
773 private void programIcmpv6NaForwardFlows(Uuid networkId, VirtualSubnet subnet, int action) {
774 if (!ipv6ServiceEosHandler.isClusterOwner()) {
775 LOG.trace("Not a cluster Owner, skip flow programming.");
778 if (subnet == null) {
779 LOG.debug("Subnet is null, skipping programIcmpv6NaForwardFlows.");
783 VirtualNetwork vnet = getNetwork(networkId);
785 if (!Ipv6ServiceUtils.isIpv6Subnet(subnet)) {
788 Long elanTag = getNetworkElanTag(networkId);
789 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
790 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
791 if (dpnIfaceInfo.getDpId() == null) {
794 List<VirtualPort> vmPorts = getVmPortsInSubnetByDpId(subnet.getSubnetUUID(), dpnIfaceInfo.getDpId());
795 for (VirtualPort vmPort : vmPorts) {
796 programIcmpv6NaForwardFlow(vmPort, dpnIfaceInfo.getDpId(), elanTag, action);
800 addUnprocessedNaFlows(networkId, Sets.newHashSet(subnet), action);
805 * Programs ICMPv6 NA forwarding flow for fixed IPs of neutron port. NA's from non-fixed IPs are
806 * punted to controller for learning.
808 * @param vmPort the VM port
809 * @param dpId the DP ID
810 * @param elanTag the ELAN tag
811 * @param addOrRemove the add or remove flag
813 private void programIcmpv6NaForwardFlow(IVirtualPort vmPort, Uint64 dpId, Long elanTag, int addOrRemove) {
814 ipv6ServiceUtils.installIcmpv6NaForwardFlow(NwConstants.IPV6_TABLE, vmPort, dpId, elanTag, addOrRemove);
817 public List<VirtualPort> getVmPortsInSubnetByDpId(Uuid snetId, Uint64 dpId) {
818 List<VirtualPort> vmPorts = new ArrayList<>();
819 for (VirtualPort port : vintfs.values()) {
820 if (dpId.equals(port.getDpId()) && Ipv6ServiceUtils.isVmPort(port.getDeviceOwner())) {
821 for (VirtualSubnet subnet : port.getSubnets()) {
822 if (subnet.getSubnetUUID().equals(snetId)) {
831 public String getInterfaceNameFromTag(long portTag) {
832 String interfaceName = null;
833 GetInterfaceFromIfIndexInput input = new GetInterfaceFromIfIndexInputBuilder()
834 .setIfIndex((int) portTag).build();
835 Future<RpcResult<GetInterfaceFromIfIndexOutput>> futureOutput =
836 interfaceManagerRpc.getInterfaceFromIfIndex(input);
838 GetInterfaceFromIfIndexOutput output = futureOutput.get().getResult();
839 interfaceName = output.getInterfaceName();
840 } catch (InterruptedException | ExecutionException e) {
841 LOG.error("Error while retrieving the interfaceName from tag using getInterfaceFromIfIndex RPC");
843 LOG.trace("Returning interfaceName {} for tag {} form getInterfaceNameFromTag", interfaceName, portTag);
844 return interfaceName;
848 public Long updateNetworkElanTag(Uuid networkId) {
850 if (null != this.elanProvider) {
851 ElanInstance elanInstance = this.elanProvider.getElanInstance(networkId.getValue());
852 if (null != elanInstance) {
853 elanTag = elanInstance.getElanTag().longValue();
854 VirtualNetwork net = getNetwork(networkId);
856 net.setElanTag(elanTag);
863 public void updateNetworkMtuInfo(Uuid networkId, int mtu) {
864 VirtualNetwork net = getNetwork(networkId);
871 private VirtualNetwork getNetwork(Uuid networkId) {
872 return networkId != null ? vnetworks.get(networkId) : null;
876 public Long getNetworkElanTag(Uuid networkId) {
878 IVirtualNetwork net = getNetwork(networkId);
880 elanTag = net.getElanTag();
881 if (null == elanTag) {
882 elanTag = updateNetworkElanTag(networkId);
889 public int getNetworkMtu(Uuid networkId) {
891 IVirtualNetwork net = getNetwork(networkId);
898 public void addNetwork(Uuid networkId, int mtu) {
899 if (networkId == null) {
903 if (vnetworks.putIfAbsent(networkId, new VirtualNetwork(networkId)) == null) {
904 updateNetworkMtuInfo(networkId, mtu);
905 updateNetworkElanTag(networkId);
907 Set<VirtualPort> intfList = removeUnprocessed(unprocessedNetIntfs, networkId);
908 if (intfList == null) {
909 LOG.info("No unprocessed interfaces for the net {}", networkId);
913 for (VirtualPort intf : intfList) {
915 updateInterfaceDpidOfPortInfo(intf.getIntfUUID());
919 Set<Ipv6Address> ipv6Addresses =
920 removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
922 for (Ipv6Address ipv6Address : ipv6Addresses) {
923 programIcmpv6NSPuntFlowForAddress(networkId, ipv6Address, Ipv6ServiceConstants.ADD_FLOW);
926 Integer action = removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
927 programIcmpv6RSPuntFlows(networkId, action);
929 Set<VirtualSubnet> subnets = removeUnprocessedNaFlows(networkId);
930 programIcmpv6NaPuntFlow(networkId, subnets, Ipv6ServiceConstants.ADD_FLOW);
931 for (VirtualSubnet subnet : subnets) {
932 programIcmpv6NaForwardFlows(networkId, subnet, action);
936 public void removeNetwork(Uuid networkId) {
937 // Delete the network and the corresponding dpnIds<-->List(ports) cache.
938 VirtualNetwork net = networkId != null ? vnetworks.remove(networkId) : null;
939 if (null != net && null != net.getNetworkUuid()) {
940 /* removing all RS flows when network gets removed, as the DPN-list is maintained only as part of
941 * network cache. After removal of network there is no way to remove them today. */
942 programIcmpv6RSPuntFlows(net.getNetworkUuid(), Ipv6ServiceConstants.DEL_FLOW);
943 removeAllIcmpv6NSPuntFlowForNetwork(net.getNetworkUuid());
946 removeUnprocessed(unprocessedNetIntfs, networkId);
947 removeUnprocessedRSFlows(unprocessedNetRSFlowIntfs, networkId);
948 removeUnprocessedNSFlows(unprocessedNetNSFlowIntfs, networkId);
951 private void transmitRouterAdvertisement(VirtualPort intf, Ipv6RouterAdvertisementType advType) {
952 Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt(packetService, this);
954 VirtualNetwork vnet = getNetwork(intf.getNetworkID());
956 long elanTag = vnet.getElanTag();
957 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
958 /* For UNSOLICITED_ADVERTISEMENT and CEASE_ADVERTISEMENT invoking transmitRtrAdvertisement()
959 * one time is sufficient. Since using IPv6 network ELAN BC group it will send an RA packetOut
960 * information to all VMs OF port which are booted on that particular ELAN Group
962 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
963 LOG.debug("transmitRouterAdvertisement: Transmitting RA {} for ELAN Tag {}",
965 if (dpnIfaceInfo.getDpId() != null) {
966 ipv6RouterAdvert.transmitRtrAdvertisement(advType, intf, elanTag, null,
967 dpnIfaceInfo.getDpId(), intf.getIntfUUID());
975 private void removeAllIcmpv6NSPuntFlowForNetwork(Uuid networkId) {
976 Long elanTag = getNetworkElanTag(networkId);
977 VirtualNetwork vnet = vnetworks.get(networkId);
982 Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
984 for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
985 for (Ipv6Address ipv6Address : dpnIfaceInfo.ndTargetFlowsPunted) {
986 if (ipv6ServiceEosHandler.isClusterOwner()) {
987 ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
988 elanTag, ipv6Address, Ipv6ServiceConstants.DEL_FLOW);
990 dpnIfaceInfo.updateNDTargetAddress(ipv6Address, Ipv6ServiceConstants.DEL_ENTRY);
996 public void transmitUnsolicitedRA(Uuid portId) {
997 VirtualPort port = getPort(portId);
998 LOG.debug("in transmitUnsolicitedRA for {}, port {}", portId, port);
1000 transmitUnsolicitedRA(port);
1004 public void transmitUnsolicitedRA(VirtualPort port) {
1005 if (ipv6ServiceEosHandler.isClusterOwner()) {
1006 /* Only the Cluster Owner would be sending out the Periodic RAs.
1007 However, the timer is configured on all the nodes to handle cluster fail-over scenarios.
1009 transmitRouterAdvertisement(port, Ipv6RouterAdvertisementType.UNSOLICITED_ADVERTISEMENT);
1011 Timeout portTimeout = timer.setPeriodicTransmissionTimeout(port.getPeriodicTimer(),
1012 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL,
1014 port.setPeriodicTimeout(portTimeout);
1015 LOG.debug("re-started periodic RA Timer for routerIntf {}, int {}s", port.getIntfUUID(),
1016 Ipv6ServiceConstants.PERIODIC_RA_INTERVAL);
1020 public List<IVirtualPort> getInterfaceCache() {
1021 List<IVirtualPort> virtualPorts = new ArrayList<>();
1022 for (VirtualPort vport: vintfs.values()) {
1023 virtualPorts.add(vport);
1025 return virtualPorts;
1029 public List<IVirtualNetwork> getNetworkCache() {
1030 List<IVirtualNetwork> virtualNetworks = new ArrayList<>();
1031 for (VirtualNetwork vnet: vnetworks.values()) {
1032 virtualNetworks.add(vnet);
1034 return virtualNetworks;
1038 public List<IVirtualSubnet> getSubnetCache() {
1039 List<IVirtualSubnet> virtualSubnets = new ArrayList<>();
1040 for (VirtualSubnet vsubnet: vsubnets.values()) {
1041 virtualSubnets.add(vsubnet);
1043 return virtualSubnets;
1047 public List<IVirtualRouter> getRouterCache() {
1048 List<IVirtualRouter> virtualRouter = new ArrayList<>();
1049 for (VirtualRouter vrouter: vrouters.values()) {
1050 virtualRouter.add(vrouter);
1052 return virtualRouter;
1055 public List<Action> getEgressAction(String interfaceName) {
1056 List<Action> actions = null;
1058 GetEgressActionsForInterfaceInputBuilder egressAction =
1059 new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName);
1060 Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
1061 interfaceManagerRpc.getEgressActionsForInterface(egressAction.build());
1062 RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
1063 if (!rpcResult.isSuccessful()) {
1064 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
1065 interfaceName, rpcResult.getErrors());
1067 actions = rpcResult.getResult().getAction();
1069 } catch (InterruptedException | ExecutionException e) {
1070 LOG.warn("Exception when egress actions for interface {}", interfaceName, e);