2 * Copyright (c) 2014 - 2016 Red Hat, 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.ovsdb.openstack.netvirt.impl;
11 import com.google.common.base.Preconditions;
12 import org.apache.commons.lang3.tuple.ImmutablePair;
13 import org.apache.commons.lang3.tuple.Pair;
14 import org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent;
15 import org.opendaylight.ovsdb.openstack.netvirt.AbstractHandler;
16 import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
17 import org.opendaylight.ovsdb.openstack.netvirt.NeutronL3AdapterEvent;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
19 import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
20 import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
21 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
22 import org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher;
23 import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
24 import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
25 import org.opendaylight.ovsdb.openstack.netvirt.api.IcmpEchoProvider;
26 import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
27 import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
28 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
29 import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
30 import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
31 import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager;
32 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
33 import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
34 import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
35 import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
36 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP;
37 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork;
38 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort;
39 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter;
40 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter_Interface;
41 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup;
42 import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSubnet;
43 import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs;
44 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronFloatingIPCRUD;
45 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronNetworkCRUD;
46 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronPortCRUD;
47 import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronSubnetCRUD;
48 import org.opendaylight.ovsdb.openstack.netvirt.translator.iaware.impl.NeutronIAwareUtil;
49 import org.opendaylight.ovsdb.utils.neutron.utils.NeutronModelsDataStoreHelper;
50 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.osgi.framework.ServiceReference;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
65 import java.net.InetAddress;
66 import java.net.UnknownHostException;
67 import java.util.ArrayList;
68 import java.util.Collection;
69 import java.util.HashMap;
70 import java.util.HashSet;
71 import java.util.Iterator;
72 import java.util.List;
77 * Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
78 * these events, the abstract router callbacks can be generated to the multi-tenant aware router,
79 * as well as the multi-tenant router forwarding provider.
81 public class NeutronL3Adapter extends AbstractHandler implements GatewayMacResolverListener, ConfigInterface {
82 private static final Logger LOG = LoggerFactory.getLogger(NeutronL3Adapter.class);
84 // The implementation for each of these services is resolved by the OSGi Service Manager
85 private volatile ConfigurationService configurationService;
86 private volatile TenantNetworkManager tenantNetworkManager;
87 private volatile NodeCacheManager nodeCacheManager;
88 private volatile INeutronNetworkCRUD neutronNetworkCache;
89 private volatile INeutronSubnetCRUD neutronSubnetCache;
90 private volatile INeutronPortCRUD neutronPortCache;
91 private volatile INeutronFloatingIPCRUD neutronFloatingIpCache;
92 private volatile L3ForwardingProvider l3ForwardingProvider;
93 private volatile InboundNatProvider inboundNatProvider;
94 private volatile OutboundNatProvider outboundNatProvider;
95 private volatile ArpProvider arpProvider;
96 private volatile RoutingProvider routingProvider;
97 private volatile GatewayMacResolver gatewayMacResolver;
98 private volatile SecurityServicesManager securityServicesManager;
99 private volatile IcmpEchoProvider icmpEchoProvider;
101 private class FloatIpData {
102 // br-int of node where floating ip is associated with tenant port
103 private final Long dpid;
104 // patch port in br-int used to reach br-ex
105 private final Long ofPort;
106 // segmentation id of the net where fixed ip is instantiated
107 private final String segId;
108 // mac address assigned to neutron port of floating ip
109 private final String macAddress;
110 private final String floatingIpAddress;
111 // ip address given to tenant vm
112 private final String fixedIpAddress;
113 private final String neutronRouterMac;
115 FloatIpData(final Long dpid, final Long ofPort, final String segId, final String macAddress,
116 final String floatingIpAddress, final String fixedIpAddress, final String neutronRouterMac) {
118 this.ofPort = ofPort;
120 this.macAddress = macAddress;
121 this.floatingIpAddress = floatingIpAddress;
122 this.fixedIpAddress = fixedIpAddress;
123 this.neutronRouterMac = neutronRouterMac;
127 private Map<String, String> networkIdToRouterMacCache;
128 private Map<String, List<Neutron_IPs>> networkIdToRouterIpListCache;
129 private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
131 private Map<String, Pair<Long, Uuid>> neutronPortToDpIdCache;
132 private Map<String, FloatIpData> floatIpDataMapCache;
134 private String externalRouterMac;
135 private Boolean enabled = false;
136 private Boolean isCachePopulationDone = false;
137 private Set<NeutronPort> portCleanupCache;
139 private Southbound southbound;
140 private DistributedArpService distributedArpService;
141 private NeutronModelsDataStoreHelper neutronModelsDataStoreHelper;
143 private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
144 private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
145 private static final String OWNER_ROUTER_GATEWAY = "network:router_gateway";
146 private static final String OWNER_FLOATING_IP = "network:floatingip";
147 private static final String DEFAULT_EXT_RTR_MAC = "00:00:5E:00:01:01";
149 public NeutronL3Adapter(NeutronModelsDataStoreHelper neutronHelper) {
150 LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
151 this.neutronModelsDataStoreHelper = neutronHelper;
154 private void initL3AdapterMembers() {
155 Preconditions.checkNotNull(configurationService);
157 if (configurationService.isL3ForwardingEnabled()) {
158 this.networkIdToRouterMacCache = new HashMap<>();
159 this.networkIdToRouterIpListCache = new HashMap<>();
160 this.subnetIdToRouterInterfaceCache = new HashMap<>();
161 this.neutronPortToDpIdCache = new HashMap<>();
162 this.floatIpDataMapCache = new HashMap<>();
164 this.externalRouterMac = configurationService.getDefaultGatewayMacAddress(null);
165 if (this.externalRouterMac == null) {
166 this.externalRouterMac = DEFAULT_EXT_RTR_MAC;
169 LOG.info("OVSDB L3 forwarding is enabled");
171 LOG.debug("OVSDB L3 forwarding is disabled");
173 this.portCleanupCache = new HashSet<>();
177 // Callbacks from AbstractHandler
180 public void processEvent(AbstractEvent abstractEvent) {
181 if (!(abstractEvent instanceof NeutronL3AdapterEvent)) {
182 LOG.error("Unable to process abstract event " + abstractEvent);
189 NeutronL3AdapterEvent ev = (NeutronL3AdapterEvent) abstractEvent;
190 switch (ev.getAction()) {
192 if (ev.getSubType() == NeutronL3AdapterEvent.SubType.SUBTYPE_EXTERNAL_MAC_UPDATE) {
193 updateExternalRouterMac( ev.getMacAddress().getValue() );
195 LOG.warn("Received update for an unexpected event " + ev);
205 LOG.warn("Unable to process event " + ev);
211 // Callbacks from GatewayMacResolverListener
215 public void gatewayMacResolved(Long externalNetworkBridgeDpid, IpAddress gatewayIpAddress, MacAddress macAddress) {
216 LOG.info("got gatewayMacResolved callback for ip {} on dpid {} to mac {}",
217 gatewayIpAddress, externalNetworkBridgeDpid, macAddress);
222 if (macAddress == null || macAddress.getValue() == null) {
223 // TODO: handle cases when mac is null
228 // Enqueue event so update is handled by adapter's thread
230 enqueueEvent( new NeutronL3AdapterEvent(externalNetworkBridgeDpid, gatewayIpAddress, macAddress) );
233 private void populateL3ForwardingCaches() {
237 if(this.isCachePopulationDone || this.neutronFloatingIpCache == null
238 || this.neutronPortCache == null ||this.neutronNetworkCache == null) {
241 this.isCachePopulationDone = true;
242 LOG.debug("Populating NetVirt L3 caches from data store configuration");
243 Routers routers = this.neutronModelsDataStoreHelper.readAllNeutronRouters();
244 Ports ports = this.neutronModelsDataStoreHelper.readAllNeutronPorts();
245 if(routers != null && routers.getRouter() != null && ports != null) {
246 LOG.debug("L3 Cache Population : {} Neutron router present in data store",routers.getRouter().size());
247 for( Router router : routers.getRouter()) {
248 LOG.debug("L3 Cache Population : Populate caches for router {}",router);
249 if(!ports.getPort().isEmpty()) {
250 for( Port port : ports.getPort()) {
251 if (port.getDeviceId().equals(router.getUuid().getValue()) &&
252 port.getDeviceOwner().equals(OWNER_ROUTER_INTERFACE)) {
253 LOG.debug("L3 Cache Population : Router interface {} found.",port);
254 networkIdToRouterMacCache.put(port.getNetworkId().getValue()
255 , port.getMacAddress());
257 networkIdToRouterIpListCache.put(port.getNetworkId().getValue(),
258 NeutronIAwareUtil.convertMDSalIpToNeutronIp(port.getFixedIps()));
259 subnetIdToRouterInterfaceCache.put(port.getFixedIps().get(0).getSubnetId().getValue(),
260 NeutronIAwareUtil.convertMDSalInterfaceToNeutronRouterInterface(port));
264 LOG.warn("L3 Cache Population :Did not find any port information " +
265 "in config Data Store for router {}",router);
269 LOG.debug("NetVirt L3 caches population is done");
272 private Pair<Long, Uuid> getDpIdOfNeutronPort(String neutronTenantPortUuid) {
273 if(neutronPortToDpIdCache.get(neutronTenantPortUuid) == null) {
274 List<Node> bridges = this.southbound.readOvsdbTopologyBridgeNodes();
275 LOG.debug("getDpIdOfNeutronPort : {} bridges present in ovsdb topology",bridges.size());
276 for(Node bridge : bridges) {
277 List<OvsdbTerminationPointAugmentation> interfaces
278 = southbound.extractTerminationPointAugmentations(bridge);
279 if(interfaces != null && !interfaces.isEmpty()) {
280 LOG.debug("getDpIdOfNeutronPort : {} termination point present on bridge {}",
281 interfaces.size(), bridge.getNodeId());
282 for (OvsdbTerminationPointAugmentation intf : interfaces) {
283 NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
284 if(neutronPort != null && neutronPort.getID().equals(neutronTenantPortUuid)) {
285 Long dpId = getDpidForIntegrationBridge(bridge);
286 Uuid interfaceUuid = intf.getInterfaceUuid();
287 LOG.debug("getDpIdOfNeutronPort : Found bridge {} and interface {} for the tenant neutron" +
288 " port {}",dpId,interfaceUuid,neutronTenantPortUuid);
289 handleInterfaceEventAdd(neutronPort.getPortUUID(), dpId, interfaceUuid);
296 return neutronPortToDpIdCache.get(neutronTenantPortUuid);
299 private Collection<FloatIpData> getAllFloatingIPsWithMetadata() {
300 LOG.debug("getAllFloatingIPsWithMetadata : Fechting all floating Ips and it's metadata");
301 List<NeutronFloatingIP> neutronFloatingIps = neutronFloatingIpCache.getAllFloatingIPs();
302 if(neutronFloatingIps != null && !neutronFloatingIps.isEmpty()) {
303 for (NeutronFloatingIP neutronFloatingIP : neutronFloatingIps) {
304 if(!floatIpDataMapCache.containsKey(neutronFloatingIP.getID())){
305 LOG.debug("Metadata for floating ip {} is not present in the cache. " +
306 "Fetching from data store.",neutronFloatingIP.getID());
307 this.getFloatingIPWithMetadata(neutronFloatingIP.getID());
311 LOG.debug("getAllFloatingIPsWithMetadata : {} floating points found in data store",floatIpDataMapCache.size());
312 return floatIpDataMapCache.values();
314 private FloatIpData getFloatingIPWithMetadata(String neutronFloatingId) {
315 LOG.debug("getFloatingIPWithMetadata : Get Floating ip and it's meta data for neutron " +
316 "floating id {} ",neutronFloatingId);
317 if(floatIpDataMapCache.get(neutronFloatingId) == null) {
318 NeutronFloatingIP neutronFloatingIP = neutronFloatingIpCache.getFloatingIP(neutronFloatingId);
319 if (neutronFloatingIP == null) {
320 LOG.error("getFloatingIPWithMetadata : Floating ip {} is missing from data store, that should not happen",neutronFloatingId);
323 List<NeutronPort> neutronPorts = neutronPortCache.getAllPorts();
324 NeutronPort neutronPortForFloatIp = null;
325 for (NeutronPort neutronPort : neutronPorts) {
326 if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
327 neutronPort.getDeviceID().equals(neutronFloatingIP.getID())) {
328 neutronPortForFloatIp = neutronPort;
333 String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
334 if(neutronTenantPortUuid == null) {
337 Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
338 String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
339 String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
340 String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
342 NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
343 NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
344 neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
345 String providerSegmentationId = tenantNeutronNetwork != null ?
346 tenantNeutronNetwork.getProviderSegmentationID() : null;
347 String neutronRouterMac = tenantNeutronNetwork != null ?
348 networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
350 if (nodeIfPair == null || neutronTenantPortUuid == null ||
351 providerSegmentationId == null || providerSegmentationId.isEmpty() ||
352 floatingIpMac == null || floatingIpMac.isEmpty() ||
353 neutronRouterMac == null || neutronRouterMac.isEmpty()) {
354 LOG.debug("getFloatingIPWithMetadata :Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} " +
355 "seg {} mac {} rtrMac {}",
358 neutronPortForFloatIp,
359 neutronTenantPortUuid,
360 providerSegmentationId,
367 // get ofport for patch port in br-int
368 final Long dpId = nodeIfPair.getLeft();
369 final Long ofPort = findOFPortForExtPatch(dpId);
370 if (ofPort == null) {
371 LOG.warn("getFloatingIPWithMetadata : Unable to locate OF port of patch port " +
372 "to connect floating ip to external bridge. dpid {}",
377 final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
378 floatingIpAddress, fixedIpAddress, neutronRouterMac);
379 floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
382 return floatIpDataMapCache.get(neutronFloatingId);
385 * Invoked to configure the mac address for the external gateway in br-ex. ovsdb netvirt needs help in getting
386 * mac for given ip in br-ex (bug 3378). For example, since ovsdb has no real arp, it needs a service in can
387 * subscribe so that the mac address associated to the gateway ip address is available.
389 * @param externalRouterMacUpdate The mac address to be associated to the gateway.
391 public void updateExternalRouterMac(final String externalRouterMacUpdate) {
392 Preconditions.checkNotNull(externalRouterMacUpdate);
394 flushExistingIpRewrite();
395 this.externalRouterMac = externalRouterMacUpdate;
396 rebuildExistingIpRewrite();
402 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
403 * @param subnet An instance of NeutronSubnet object.
405 public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
406 LOG.debug("Neutron subnet {} event : {}", action, subnet.toString());
410 * Process the port event as a router interface event.
411 * For a not delete action, since a port is only create when the tennat uses the subnet, it is required to
412 * verify if all routers across all nodes have the interface for the port's subnet(s) configured.
414 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
415 * @param neutronPort An instance of NeutronPort object.
417 public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
418 LOG.debug("Neutron port {} event : {}", action, neutronPort.toString());
420 if (action == Action.UPDATE) {
421 // FIXME: Bug 4971 Move cleanup cache to SG Impl
422 this.updatePortInCleanupCache(neutronPort, neutronPort.getOriginalPort());
423 this.processSecurityGroupUpdate(neutronPort);
430 final boolean isDelete = action == Action.DELETE;
432 if (action == Action.DELETE) {
433 // Bug 5164: Cleanup Floating IP OpenFlow Rules when port is deleted.
434 this.cleanupFloatingIPRules(neutronPort);
437 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
439 LOG.info("Port {} is network router gateway interface, "
440 + "triggering gateway resolution for the attached external network", neutronPort);
441 this.triggerGatewayMacResolver(neutronPort);
443 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
445 if (externalNetwork != null && externalNetwork.isRouterExternal()) {
446 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
448 if (externalSubnet != null &&
449 externalSubnet.getIpVersion() == 4) {
450 gatewayMacResolver.stopPeriodicRefresh(new Ipv4Address(externalSubnet.getGatewayIP()));
456 // Treat the port event as a router interface event if the port belongs to router. This is a
457 // helper for handling cases when handleNeutronRouterInterfaceEvent is not available
459 if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
460 neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
462 if (neutronPort.getFixedIPs() != null) {
463 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
464 NeutronRouter_Interface neutronRouterInterface =
465 new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
466 // id of router interface to be same as subnet
467 neutronRouterInterface.setID(neutronIP.getSubnetUUID());
468 neutronRouterInterface.setTenantID(neutronPort.getTenantID());
470 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
474 // We made it here, port is not used as a router interface. If this is not a delete action, make sure that
475 // all nodes that are supposed to have a router interface for the port's subnet(s), have it configured. We
476 // need to do this check here because a router interface is not added to a node until tenant becomes needed
479 if (!isDelete && neutronPort.getFixedIPs() != null) {
480 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
481 NeutronRouter_Interface neutronRouterInterface =
482 subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
483 if (neutronRouterInterface != null) {
484 this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
488 this.updateL3ForNeutronPort(neutronPort, isDelete);
495 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
496 * @param neutronRouter An instance of NeutronRouter object.
498 public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
499 LOG.debug("Neutron router {} event : {}", action, neutronRouter.toString());
503 * Process the event enforcing actions and verifying dependencies between all router's interface. For example,
504 * delete the ports on the same subnet.
506 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
507 * @param neutronRouter An instance of NeutronRouter object.
508 * @param neutronRouterInterface An instance of NeutronRouter_Interface object.
510 public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
511 final NeutronRouter_Interface neutronRouterInterface,
513 LOG.debug("Router interface {} got event {}. Subnet {}",
514 neutronRouterInterface.getPortUUID(),
516 neutronRouterInterface.getSubnetUUID());
521 final boolean isDelete = action == Action.DELETE;
523 this.programFlowsForNeutronRouterInterface(neutronRouterInterface, isDelete);
525 // As neutron router interface is added/removed, we need to iterate through all the neutron ports and
526 // see if they are affected by l3
528 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
529 boolean currPortShouldBeDeleted = false;
530 // Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
532 if (neutronPort.getFixedIPs() != null) {
533 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
534 if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
535 currPortShouldBeDeleted = true;
541 this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
546 * Bug 4277: Remove the router interface cache only after deleting the neutron port l3 flows.
548 this.cleanupRouterCache(neutronRouterInterface);
553 * Invoked when a neutron message regarding the floating ip association is sent to odl via ml2. If the action is
554 * a creation, it will first add ARP rules for the given floating ip and then configure the DNAT (rewrite the
555 * packets from the floating IP address to the internal fixed ip) rules on OpenFlow Table 30 and SNAT rules (other
556 * way around) on OpenFlow Table 100.
558 * @param actionIn the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
559 * @param neutronFloatingIP An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP} instance of NeutronFloatingIP object.
561 public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
563 Preconditions.checkNotNull(neutronFloatingIP);
565 LOG.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
566 neutronFloatingIP.getFixedIPAddress(),
567 neutronFloatingIP.getFloatingIPAddress(),
568 neutronFloatingIP.getFloatingNetworkUUID());
575 // Consider action to be delete if getFixedIPAddress is null
577 if (neutronFloatingIP.getFixedIPAddress() == null) {
578 action = Action.DELETE;
583 // this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
585 if (action != Action.DELETE) {
586 // must be first, as it updates floatIpDataMapCache
587 programFlowsForFloatingIPArpAdd(neutronFloatingIP);
589 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD);
590 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD);
592 programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE);
593 programFlowsForFloatingIPInbound(neutronFloatingIP, Action.DELETE);
595 // must be last, as it updates floatIpDataMapCache
596 programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID());
601 * This method performs creation or deletion of in-bound rules into Table 30 for a existing available floating
602 * ip, otherwise for newer one.
604 private void programFlowsForFloatingIPInbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
605 Preconditions.checkNotNull(neutronFloatingIP);
607 final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
609 LOG.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
610 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
613 programInboundIpRewriteStage1(fid.dpid, fid.ofPort, fid.segId, fid.floatingIpAddress, fid.fixedIpAddress,
618 * This method performs creation or deletion of out-bound rules into Table 100 for a existing available floating
619 * ip, otherwise for newer one.
621 private void programFlowsForFloatingIPOutbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
622 Preconditions.checkNotNull(neutronFloatingIP);
624 final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
626 LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
627 action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
630 programOutboundIpRewriteStage1(fid, action);
633 private void flushExistingIpRewrite() {
634 for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
635 programOutboundIpRewriteStage1(fid, Action.DELETE);
639 private void rebuildExistingIpRewrite() {
640 for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
641 programOutboundIpRewriteStage1(fid, Action.ADD);
646 * This method creates ARP response rules into OpenFlow Table 30 for a given floating ip. In order to connect
647 * to br-ex from br-int, a patch-port is used. Thus, the patch-port will be responsible to respond the ARP
650 private void programFlowsForFloatingIPArpAdd(final NeutronFloatingIP neutronFloatingIP) {
651 Preconditions.checkNotNull(neutronFloatingIP);
652 Preconditions.checkNotNull(neutronFloatingIP.getFixedIPAddress());
653 Preconditions.checkNotNull(neutronFloatingIP.getFloatingIPAddress());
655 // find bridge Node where floating ip is configured by looking up cache for its port
656 final NeutronPort neutronPortForFloatIp = findNeutronPortForFloatingIp(neutronFloatingIP.getID());
657 final String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
658 final Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
659 final String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
660 final String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
661 final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
663 final NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
664 final NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
665 neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
666 final String providerSegmentationId = tenantNeutronNetwork != null ?
667 tenantNeutronNetwork.getProviderSegmentationID() : null;
668 final String neutronRouterMac = tenantNeutronNetwork != null ?
669 networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
671 if (nodeIfPair == null || neutronTenantPortUuid == null ||
672 providerSegmentationId == null || providerSegmentationId.isEmpty() ||
673 floatingIpMac == null || floatingIpMac.isEmpty() ||
674 neutronRouterMac == null || neutronRouterMac.isEmpty()) {
675 LOG.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
678 neutronPortForFloatIp,
679 neutronTenantPortUuid,
680 providerSegmentationId,
686 // get ofport for patch port in br-int
687 final Long dpId = nodeIfPair.getLeft();
688 final Long ofPort = findOFPortForExtPatch(dpId);
689 if (ofPort == null) {
690 LOG.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
695 // Respond to ARPs for the floating ip address by default, via the patch port that connects br-int to br-ex
697 if (distributedArpService.programStaticRuleStage1(dpId, encodeExcplicitOFPort(ofPort), floatingIpMac, floatingIpAddress,
699 final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
700 floatingIpAddress, fixedIpAddress, neutronRouterMac);
701 floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
702 LOG.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
703 neutronFloatingIP.getFixedIPAddress(), neutronFloatingIP.getFloatingIPAddress(),
704 floatingIpMac, ofPort, providerSegmentationId, dpId);
708 private void programFlowsForFloatingIPArpDelete(final String neutronFloatingIPUuid) {
709 final FloatIpData floatIpData = getFloatingIPWithMetadata(neutronFloatingIPUuid);
710 if (floatIpData == null) {
711 LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
715 if (distributedArpService.programStaticRuleStage1(floatIpData.dpid, encodeExcplicitOFPort(floatIpData.ofPort), floatIpData.macAddress,
716 floatIpData.floatingIpAddress, Action.DELETE)) {
717 floatIpDataMapCache.remove(neutronFloatingIPUuid);
718 LOG.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
719 floatIpData.floatingIpAddress, floatIpData.macAddress, floatIpData.ofPort, floatIpData.dpid);
723 private NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
724 for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
725 if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
726 neutronPort.getDeviceID().equals(floatingIpUuid)) {
733 private Long findOFPortForExtPatch(Long dpId) {
734 final String brInt = configurationService.getIntegrationBridgeName();
735 final String brExt = configurationService.getExternalBridgeName();
736 final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
738 Preconditions.checkNotNull(dpId);
739 Preconditions.checkNotNull(portNameInt);
741 final long dpidPrimitive = dpId;
742 for (Node node : nodeCacheManager.getBridgeNodes()) {
743 if (dpidPrimitive == southbound.getDataPathId(node)) {
744 final OvsdbTerminationPointAugmentation terminationPointOfBridge =
745 southbound.getTerminationPointOfBridge(node, portNameInt);
746 return terminationPointOfBridge == null ? null : terminationPointOfBridge.getOfport();
755 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
756 * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronFloatingIP object.
758 public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
759 LOG.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
763 // Callbacks from OVSDB's southbound handler
768 * @param bridgeNode An instance of Node object.
769 * @param intf An {@link org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105
770 * .OvsdbTerminationPointAugmentation} instance of OvsdbTerminationPointAugmentation object.
771 * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronNetwork
773 * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
775 public void handleInterfaceEvent(final Node bridgeNode, final OvsdbTerminationPointAugmentation intf,
776 final NeutronNetwork neutronNetwork, Action action) {
777 LOG.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
778 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork);
780 final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
781 if (action != Action.DELETE && neutronPort != null) {
782 // FIXME: Bug 4971 Move cleanup cache to SG Impl
783 storePortInCleanupCache(neutronPort);
790 final Long dpId = getDpidForIntegrationBridge(bridgeNode);
791 final Uuid interfaceUuid = intf.getInterfaceUuid();
793 LOG.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
794 action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork, neutronPort, dpId, interfaceUuid);
796 if (neutronPort != null) {
797 final String neutronPortUuid = neutronPort.getPortUUID();
799 if (action != Action.DELETE && dpId != null && interfaceUuid != null) {
800 handleInterfaceEventAdd(neutronPortUuid, dpId, interfaceUuid);
803 handleNeutronPortEvent(neutronPort, action);
806 if (action == Action.DELETE && interfaceUuid != null) {
807 handleInterfaceEventDelete(intf, dpId);
811 private void handleInterfaceEventAdd(final String neutronPortUuid, Long dpId, final Uuid interfaceUuid) {
812 neutronPortToDpIdCache.put(neutronPortUuid, new ImmutablePair<>(dpId, interfaceUuid));
813 LOG.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
814 neutronPortUuid, dpId, interfaceUuid.getValue());
817 private void handleInterfaceEventDelete(final OvsdbTerminationPointAugmentation intf, final Long dpId) {
818 // Remove entry from neutronPortToDpIdCache based on interface uuid
819 for (Map.Entry<String, Pair<Long, Uuid>> entry : neutronPortToDpIdCache.entrySet()) {
820 final String currPortUuid = entry.getKey();
821 if (intf.getInterfaceUuid().equals(entry.getValue().getRight())) {
822 LOG.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
823 currPortUuid, dpId, intf.getInterfaceUuid().getValue());
824 neutronPortToDpIdCache.remove(currPortUuid);
833 private void updateL3ForNeutronPort(final NeutronPort neutronPort, final boolean isDelete) {
835 final String networkUUID = neutronPort.getNetworkUUID();
836 final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
838 // If there is no router interface handling the networkUUID, we are done
839 if (routerMacAddress == null || routerMacAddress.isEmpty()) {
843 // If this is the neutron port for the router interface itself, ignore it as well. Ports that represent the
844 // router interface are handled via handleNeutronRouterInterfaceEvent.
845 if (routerMacAddress.equalsIgnoreCase(neutronPort.getMacAddress())) {
849 final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
850 final String providerSegmentationId = neutronNetwork != null ?
851 neutronNetwork.getProviderSegmentationID() : null;
852 final String tenantMac = neutronPort.getMacAddress();
854 if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
855 tenantMac == null || tenantMac.isEmpty()) {
856 // done: go no further w/out all the info needed...
860 final Action action = isDelete ? Action.DELETE : Action.ADD;
861 List<Node> nodes = nodeCacheManager.getBridgeNodes();
862 if (nodes.isEmpty()) {
863 LOG.trace("updateL3ForNeutronPort has no nodes to work with");
865 for (Node node : nodes) {
866 final Long dpid = getDpidForIntegrationBridge(node);
870 if (neutronPort.getFixedIPs() == null) {
873 for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
874 final String tenantIpStr = neutronIP.getIpAddress();
875 if (tenantIpStr.isEmpty()) {
879 // Configure L3 fwd. We do that regardless of tenant network present, because these rules are
880 // still needed when routing to subnets non-local to node (bug 2076).
881 programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
886 private void processSecurityGroupUpdate(NeutronPort neutronPort) {
887 LOG.trace("processSecurityGroupUpdate:" + neutronPort);
889 * Get updated data and original data for the the changed. Identify the security groups that got
890 * added and removed and call the appropriate providers for updating the flows.
893 NeutronPort originalPort = neutronPort.getOriginalPort();
894 List<NeutronSecurityGroup> addedGroup = getsecurityGroupChanged(neutronPort,
895 neutronPort.getOriginalPort());
896 List<NeutronSecurityGroup> deletedGroup = getsecurityGroupChanged(neutronPort.getOriginalPort(),
899 if (null != addedGroup && !addedGroup.isEmpty()) {
900 securityServicesManager.syncSecurityGroup(neutronPort,addedGroup,true);
902 if (null != deletedGroup && !deletedGroup.isEmpty()) {
903 securityServicesManager.syncSecurityGroup(neutronPort,deletedGroup,false);
906 } catch (Exception e) {
907 LOG.error("Exception in processSecurityGroupUpdate", e);
911 private List<NeutronSecurityGroup> getsecurityGroupChanged(NeutronPort port1, NeutronPort port2) {
912 LOG.trace("getsecurityGroupChanged:" + "Port1:" + port1 + "Port2" + port2);
916 List<NeutronSecurityGroup> list1 = new ArrayList<>(port1.getSecurityGroups());
920 List<NeutronSecurityGroup> list2 = new ArrayList<>(port2.getSecurityGroups());
921 for (Iterator<NeutronSecurityGroup> iterator = list1.iterator(); iterator.hasNext();) {
922 NeutronSecurityGroup securityGroup1 = iterator.next();
923 for (NeutronSecurityGroup securityGroup2 :list2) {
924 if (securityGroup1.getID().equals(securityGroup2.getID())) {
932 private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
933 String macAddress, String ipStr,
934 Action actionForNode) {
935 if (actionForNode == Action.DELETE) {
936 LOG.trace("Deleting Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
937 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
939 if (actionForNode == Action.ADD) {
940 LOG.trace("Adding Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
941 node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
944 this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
945 macAddress, ipStr, actionForNode);
948 private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
951 Action actionForNode) {
954 InetAddress inetAddress = InetAddress.getByName(address);
955 status = l3ForwardingProvider == null ?
956 new Status(StatusCode.SUCCESS) :
957 l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
958 inetAddress, macAddress, actionForNode);
959 } catch (UnknownHostException e) {
960 status = new Status(StatusCode.BADREQUEST);
963 if (status.isSuccess()) {
964 LOG.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
965 l3ForwardingProvider == null ? "skipped" : "programmed",
966 macAddress, address, node.getNodeId().getValue(), actionForNode);
968 LOG.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
969 macAddress, address, node.getNodeId().getValue(), actionForNode, status);
976 private void programFlowsForNeutronRouterInterface(final NeutronRouter_Interface destNeutronRouterInterface,
978 Preconditions.checkNotNull(destNeutronRouterInterface);
980 final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
981 String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
982 List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
983 final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
984 final NeutronNetwork neutronNetwork = subnet != null ?
985 neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
986 final String destinationSegmentationId = neutronNetwork != null ?
987 neutronNetwork.getProviderSegmentationID() : null;
988 final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
989 final String cidr = subnet != null ? subnet.getCidr() : null;
990 final int mask = getMaskLenFromCidr(cidr);
992 LOG.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
993 destNeutronRouterInterface, isDelete);
995 // in delete path, mac address as well as ip address are not provided. Being so, let's find them from
997 if (neutronNetwork != null) {
998 if (macAddress == null || macAddress.isEmpty()) {
999 macAddress = networkIdToRouterMacCache.get(neutronNetwork.getNetworkUUID());
1001 if (ipList == null || ipList.isEmpty()) {
1002 ipList = networkIdToRouterIpListCache.get(neutronNetwork.getNetworkUUID());
1006 if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
1007 cidr == null || cidr.isEmpty() ||
1008 macAddress == null || macAddress.isEmpty() ||
1009 ipList == null || ipList.isEmpty()) {
1010 LOG.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
1011 destinationSegmentationId, cidr, macAddress, ipList);
1012 // done: go no further w/out all the info needed...
1016 final Action actionForNode = isDelete ? Action.DELETE : Action.ADD;
1018 // Keep cache for finding router's mac from network uuid -- add
1021 networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
1022 networkIdToRouterIpListCache.put(neutronNetwork.getNetworkUUID(), new ArrayList<>(ipList));
1023 subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
1026 List<Node> nodes = nodeCacheManager.getBridgeNodes();
1027 if (nodes.isEmpty()) {
1028 LOG.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
1030 for (Node node : nodes) {
1031 final Long dpid = getDpidForIntegrationBridge(node);
1036 for (Neutron_IPs neutronIP : ipList) {
1037 final String ipStr = neutronIP.getIpAddress();
1038 if (ipStr.isEmpty()) {
1039 LOG.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
1040 node.getNodeId().getValue(), ipStr);
1044 // Iterate through all other interfaces and add/remove reflexive flows to this interface
1046 for (NeutronRouter_Interface srcNeutronRouterInterface : subnetIdToRouterInterfaceCache.values()) {
1047 programFlowsForNeutronRouterInterfacePair(node, dpid,
1048 srcNeutronRouterInterface, destNeutronRouterInterface,
1049 neutronNetwork, destinationSegmentationId,
1050 macAddress, ipStr, mask, actionForNode,
1051 true /*isReflexsive*/);
1055 programFlowForNetworkFromExternal(node, dpid, destinationSegmentationId, macAddress, ipStr, mask,
1058 // Enable ARP responder by default, because router interface needs to be responded always.
1059 distributedArpService.programStaticRuleStage1(dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
1060 programIcmpEcho(dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
1063 // Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
1064 // for the external neutron networks.
1067 final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
1068 programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, cidr, actionForRewriteExclusion);
1072 // Keep cache for finding router's mac from network uuid -- NOTE: remove is done later, via cleanupRouterCache()
1075 private void programFlowForNetworkFromExternal(final Node node,
1077 final String destinationSegmentationId,
1078 final String dstMacAddress,
1079 final String destIpStr,
1081 final Action actionForNode) {
1082 programRouterInterfaceStage1(node, dpid, Constants.EXTERNAL_NETWORK, destinationSegmentationId,
1083 dstMacAddress, destIpStr, destMask, actionForNode);
1086 private void programFlowsForNeutronRouterInterfacePair(final Node node,
1088 final NeutronRouter_Interface srcNeutronRouterInterface,
1089 final NeutronRouter_Interface dstNeutronRouterInterface,
1090 final NeutronNetwork dstNeutronNetwork,
1091 final String destinationSegmentationId,
1092 final String dstMacAddress,
1093 final String destIpStr,
1095 final Action actionForNode,
1096 Boolean isReflexsive) {
1097 Preconditions.checkNotNull(srcNeutronRouterInterface);
1098 Preconditions.checkNotNull(dstNeutronRouterInterface);
1100 final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
1101 if (sourceSubnetId == null) {
1102 LOG.error("Could not get provider Subnet ID from router interface {}",
1103 srcNeutronRouterInterface.getID());
1107 final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
1108 final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
1109 if (sourceNetworkId == null) {
1110 LOG.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
1114 final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
1115 if (sourceNetwork == null) {
1116 LOG.error("Could not get provider Network for Network ID {}", sourceNetworkId);
1120 if (! sourceNetwork.getTenantID().equals(dstNeutronNetwork.getTenantID())) {
1121 // Isolate subnets from different tenants within the same router
1124 final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
1125 if (sourceSegmentationId == null) {
1126 LOG.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
1129 if (sourceSegmentationId.equals(destinationSegmentationId)) {
1134 programRouterInterfaceStage1(node, dpid, sourceSegmentationId, destinationSegmentationId,
1135 dstMacAddress, destIpStr, destMask, actionForNode);
1137 // Flip roles src->dst; dst->src
1139 final NeutronPort sourceNeutronPort = neutronPortCache.getPort(srcNeutronRouterInterface.getPortUUID());
1140 final String macAddress2 = sourceNeutronPort != null ? sourceNeutronPort.getMacAddress() : null;
1141 final List<Neutron_IPs> ipList2 = sourceNeutronPort != null ? sourceNeutronPort.getFixedIPs() : null;
1142 final String cidr2 = sourceSubnet.getCidr();
1143 final int mask2 = getMaskLenFromCidr(cidr2);
1145 if (cidr2 == null || cidr2.isEmpty() ||
1146 macAddress2 == null || macAddress2.isEmpty() ||
1147 ipList2 == null || ipList2.isEmpty()) {
1148 LOG.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
1149 sourceSegmentationId, cidr2, macAddress2, ipList2);
1150 // done: go no further w/out all the info needed...
1154 for (Neutron_IPs neutronIP2 : ipList2) {
1155 final String ipStr2 = neutronIP2.getIpAddress();
1156 if (ipStr2.isEmpty()) {
1159 programFlowsForNeutronRouterInterfacePair(node, dpid, dstNeutronRouterInterface,
1160 srcNeutronRouterInterface,
1161 sourceNetwork, sourceSegmentationId,
1162 macAddress2, ipStr2, mask2, actionForNode,
1163 false /*isReflexsive*/);
1168 private void programRouterInterfaceStage1(Node node, Long dpid, String sourceSegmentationId,
1169 String destinationSegmentationId,
1170 String macAddress, String ipStr, int mask,
1171 Action actionForNode) {
1172 if (actionForNode == Action.DELETE) {
1173 LOG.trace("Deleting Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
1175 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1176 macAddress, ipStr, mask, actionForNode);
1178 if (actionForNode == Action.ADD) {
1179 LOG.trace("Adding Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
1181 node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1182 macAddress, ipStr, mask, actionForNode);
1185 this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
1186 macAddress, ipStr, mask, actionForNode);
1189 private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
1190 String destinationSegmentationId,
1192 String address, int mask,
1193 Action actionForNode) {
1196 InetAddress inetAddress = InetAddress.getByName(address);
1197 status = routingProvider == null ?
1198 new Status(StatusCode.SUCCESS) :
1199 routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
1200 macAddress, inetAddress, mask, actionForNode);
1201 } catch (UnknownHostException e) {
1202 status = new Status(StatusCode.BADREQUEST);
1205 if (status.isSuccess()) {
1206 LOG.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
1207 routingProvider == null ? "skipped" : "programmed",
1208 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1211 LOG.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
1212 macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
1213 actionForNode, status);
1218 private boolean programIcmpEcho(Long dpid, String segOrOfPort,
1219 String macAddress, String ipStr,
1221 if (action == Action.DELETE ) {
1222 LOG.trace("Deleting Flow : programIcmpEcho dpid {} segOrOfPort {} mac {} ip {} action {}",
1223 dpid, segOrOfPort, macAddress, ipStr, action);
1225 if (action == Action.ADD) {
1226 LOG.trace("Adding Flow : programIcmpEcho dpid {} segOrOfPort {} mac {} ip {} action {}",
1227 dpid, segOrOfPort, macAddress, ipStr, action);
1230 Status status = new Status(StatusCode.UNSUPPORTED);
1231 if (icmpEchoProvider != null){
1233 InetAddress inetAddress = InetAddress.getByName(ipStr);
1234 status = icmpEchoProvider.programIcmpEchoEntry(dpid, segOrOfPort,
1235 macAddress, inetAddress, action);
1236 } catch (UnknownHostException e) {
1237 status = new Status(StatusCode.BADREQUEST);
1241 if (status.isSuccess()) {
1242 LOG.debug("programIcmpEcho {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
1243 icmpEchoProvider == null ? "skipped" : "programmed",
1244 macAddress, ipStr, dpid, segOrOfPort, action);
1246 LOG.error("programIcmpEcho failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
1247 macAddress, ipStr, dpid, segOrOfPort, action, status);
1250 return status.isSuccess();
1253 private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId,
1254 String matchAddress, String rewriteAddress,
1256 if (action == Action.DELETE ) {
1257 LOG.trace("Deleting Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1259 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1261 if (action == Action.ADD ) {
1262 LOG.trace("Adding Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
1264 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1267 Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress,
1268 rewriteAddress, action);
1269 return status.isSuccess();
1272 private Status programInboundIpRewriteStage2(Long dpid, Long inboundOFPort, String providerSegmentationId,
1273 String matchAddress, String rewriteAddress,
1277 InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
1278 InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
1279 status = inboundNatProvider == null ?
1280 new Status(StatusCode.SUCCESS) :
1281 inboundNatProvider.programIpRewriteRule(dpid, inboundOFPort, providerSegmentationId,
1282 inetMatchAddress, inetRewriteAddress,
1284 } catch (UnknownHostException e) {
1285 status = new Status(StatusCode.BADREQUEST);
1288 if (status.isSuccess()) {
1289 final boolean isSkipped = inboundNatProvider == null;
1290 LOG.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
1291 isSkipped ? "skipped" : "programmed",
1292 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
1294 LOG.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
1296 dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action,
1302 private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, String cidr,
1303 Action actionForRewriteExclusion) {
1304 if (actionForRewriteExclusion == Action.DELETE ) {
1305 LOG.trace("Deleting Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
1306 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1308 if (actionForRewriteExclusion == Action.ADD) {
1309 LOG.trace("Adding Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
1310 node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
1313 this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,actionForRewriteExclusion);
1316 private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
1317 Action actionForNode) {
1318 final Status status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
1319 outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr, actionForNode);
1321 if (status.isSuccess()) {
1322 final boolean isSkipped = outboundNatProvider == null;
1323 LOG.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
1324 isSkipped ? "skipped" : "programmed",
1325 cidr, node.getNodeId().getValue(), actionForNode);
1327 LOG.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
1328 cidr, node.getNodeId().getValue(), actionForNode, status);
1333 private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) {
1335 if (action == Action.DELETE) {
1336 LOG.trace("Deleting Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} ",
1337 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1339 if (action == Action.ADD) {
1340 LOG.trace("Adding Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " ,
1341 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1344 this.programOutboundIpRewriteStage2(fid, action);
1347 private Status programOutboundIpRewriteStage2(FloatIpData fid, Action action) {
1350 InetAddress matchSrcAddress = InetAddress.getByName(fid.fixedIpAddress);
1351 InetAddress rewriteSrcAddress = InetAddress.getByName(fid.floatingIpAddress);
1352 status = outboundNatProvider == null ?
1353 new Status(StatusCode.SUCCESS) :
1354 outboundNatProvider.programIpRewriteRule(
1355 fid.dpid, fid.segId, fid.neutronRouterMac, matchSrcAddress, fid.macAddress,
1356 this.externalRouterMac, rewriteSrcAddress, fid.ofPort, action);
1357 } catch (UnknownHostException e) {
1358 status = new Status(StatusCode.BADREQUEST);
1361 if (status.isSuccess()) {
1362 final boolean isSkipped = outboundNatProvider == null;
1363 LOG.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1365 isSkipped ? "skipped" : "programmed",
1366 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
1368 LOG.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
1369 " action {} status:{}",
1370 fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action, status);
1375 private int getMaskLenFromCidr(String cidr) {
1379 String[] splits = cidr.split("/");
1380 if (splits.length != 2) {
1386 result = Integer.parseInt(splits[1].trim());
1387 } catch (NumberFormatException nfe) {
1393 private Long getDpidForIntegrationBridge(Node node) {
1394 // Check if node is integration bridge; and only then return its dpid
1395 if (southbound.getBridge(node, configurationService.getIntegrationBridgeName()) != null) {
1396 return southbound.getDataPathId(node);
1401 private Long getDpidForExternalBridge(Node node) {
1402 // Check if node is external bridge; and only then return its dpid
1403 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1404 return southbound.getDataPathId(node);
1409 private Node getExternalBridgeNode(){
1410 //Pickup the first node that has external bridge (br-ex).
1411 //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
1412 //the external network is reachable from every br-ex
1413 // TODO: Consider other deployment scenario, and thing of better solution.
1414 List<Node> allBridges = nodeCacheManager.getBridgeNodes();
1415 for(Node node : allBridges){
1416 if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
1423 private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
1424 if (gatewayPort.getFixedIPs() == null) {
1427 for (Neutron_IPs neutronIPs : gatewayPort.getFixedIPs()) {
1428 String subnetUUID = neutronIPs.getSubnetUUID();
1429 NeutronSubnet extSubnet = neutronSubnetCache.getSubnet(subnetUUID);
1430 if (extSubnet != null && extSubnet.getGatewayIP() != null) {
1433 if (extSubnet == null) {
1434 // TODO: when subnet is created, try again.
1435 LOG.debug("subnet {} in not found", subnetUUID);
1441 private void cleanupRouterCache(final NeutronRouter_Interface neutronRouterInterface) {
1444 * Remove the router cache only after deleting the neutron
1447 final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
1449 if (neutronPort != null) {
1450 networkIdToRouterMacCache.remove(neutronPort.getNetworkUUID());
1451 networkIdToRouterIpListCache.remove(neutronPort.getNetworkUUID());
1452 subnetIdToRouterInterfaceCache.remove(neutronRouterInterface.getSubnetUUID());
1456 private void cleanupFloatingIPRules(final NeutronPort neutronPort) {
1458 List<NeutronFloatingIP> neutronFloatingIps = neutronFloatingIpCache.getAllFloatingIPs();
1459 if (neutronFloatingIps != null && !neutronFloatingIps.isEmpty()) {
1460 for (NeutronFloatingIP neutronFloatingIP : neutronFloatingIps) {
1461 if (neutronFloatingIP.getPortUUID().equals(neutronPort.getPortUUID())) {
1462 handleNeutronFloatingIPEvent(neutronFloatingIP, Action.DELETE);
1469 private void triggerGatewayMacResolver(final NeutronPort gatewayPort){
1471 Preconditions.checkNotNull(gatewayPort);
1472 NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
1474 if(externalNetwork != null){
1475 if(externalNetwork.isRouterExternal()){
1476 final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
1478 // TODO: address IPv6 case.
1479 if (externalSubnet != null &&
1480 externalSubnet.getIpVersion() == 4 &&
1481 gatewayPort.getFixedIPs() != null) {
1482 LOG.info("Trigger MAC resolution for gateway ip {}", externalSubnet.getGatewayIP());
1484 gatewayMacResolver.resolveMacAddress(
1485 this, /* gatewayMacResolverListener */
1486 null, /* externalNetworkBridgeDpid */
1487 true, /* refreshExternalNetworkBridgeDpidIfNeeded */
1488 new Ipv4Address(externalSubnet.getGatewayIP()),
1489 new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
1490 new MacAddress(gatewayPort.getMacAddress()),
1491 true /* periodicRefresh */);
1493 LOG.warn("No gateway IP address found for external network {}", externalNetwork);
1497 LOG.warn("Neutron network not found for router interface {}", gatewayPort);
1502 private void storePortInCleanupCache(NeutronPort port) {
1503 this.portCleanupCache.add(port);
1507 private void updatePortInCleanupCache(NeutronPort updatedPort,NeutronPort originalPort) {
1508 removePortFromCleanupCache(originalPort);
1509 storePortInCleanupCache(updatedPort);
1512 public void removePortFromCleanupCache(NeutronPort port) {
1513 this.portCleanupCache.remove(port);
1516 public Set<NeutronPort> getPortCleanupCache() {
1517 return this.portCleanupCache;
1520 public NeutronPort getPortFromCleanupCache(String portid) {
1521 for (NeutronPort neutronPort : this.portCleanupCache) {
1522 if (neutronPort.getPortUUID() != null ) {
1523 if (neutronPort.getPortUUID().equals(portid)) {
1524 LOG.info("getPortFromCleanupCache: Matching NeutronPort found {}", portid);
1533 * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
1535 * @param ofPort the OF port number
1536 * @return the string with encoded OF port (example format "OFPort|999")
1538 public static String encodeExcplicitOFPort(Long ofPort) {
1539 return "OFPort|" + ofPort.toString();
1543 public void setDependencies(ServiceReference serviceReference) {
1545 (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this);
1546 eventDispatcher.eventHandlerAdded(serviceReference, this);
1547 tenantNetworkManager =
1548 (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
1549 configurationService =
1550 (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
1552 (ArpProvider) ServiceHelper.getGlobalInstance(ArpProvider.class, this);
1553 inboundNatProvider =
1554 (InboundNatProvider) ServiceHelper.getGlobalInstance(InboundNatProvider.class, this);
1555 outboundNatProvider =
1556 (OutboundNatProvider) ServiceHelper.getGlobalInstance(OutboundNatProvider.class, this);
1558 (RoutingProvider) ServiceHelper.getGlobalInstance(RoutingProvider.class, this);
1559 l3ForwardingProvider =
1560 (L3ForwardingProvider) ServiceHelper.getGlobalInstance(L3ForwardingProvider.class, this);
1561 distributedArpService =
1562 (DistributedArpService) ServiceHelper.getGlobalInstance(DistributedArpService.class, this);
1564 (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
1566 (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
1567 gatewayMacResolver =
1568 (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
1569 securityServicesManager =
1570 (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
1572 initL3AdapterMembers();
1576 public void setDependencies(Object impl) {
1577 if (impl instanceof INeutronNetworkCRUD) {
1578 neutronNetworkCache = (INeutronNetworkCRUD)impl;
1579 } else if (impl instanceof INeutronPortCRUD) {
1580 neutronPortCache = (INeutronPortCRUD)impl;
1581 } else if (impl instanceof INeutronSubnetCRUD) {
1582 neutronSubnetCache = (INeutronSubnetCRUD)impl;
1583 } else if (impl instanceof INeutronFloatingIPCRUD) {
1584 neutronFloatingIpCache = (INeutronFloatingIPCRUD)impl;
1585 } else if (impl instanceof ArpProvider) {
1586 arpProvider = (ArpProvider)impl;
1587 } else if (impl instanceof InboundNatProvider) {
1588 inboundNatProvider = (InboundNatProvider)impl;
1589 } else if (impl instanceof OutboundNatProvider) {
1590 outboundNatProvider = (OutboundNatProvider)impl;
1591 } else if (impl instanceof RoutingProvider) {
1592 routingProvider = (RoutingProvider)impl;
1593 } else if (impl instanceof L3ForwardingProvider) {
1594 l3ForwardingProvider = (L3ForwardingProvider)impl;
1595 }else if (impl instanceof GatewayMacResolver) {
1596 gatewayMacResolver = (GatewayMacResolver)impl;
1597 }else if (impl instanceof IcmpEchoProvider) {
1598 icmpEchoProvider = (IcmpEchoProvider)impl;
1601 populateL3ForwardingCaches();