*/
package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.math.BigInteger;
-import java.util.Map.Entry;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.annotation.Nullable;
-
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
+import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.JdkFutureAdapters;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static com.google.common.base.Preconditions.checkNotNull;
/**
*
implements ConfigInterface, GatewayMacResolver,PacketProcessingListener {
private static final Logger LOG = LoggerFactory.getLogger(GatewayMacResolverService.class);
- private static final short TABEL_FOR_ARP_FLOW = 0;
private static final String ARP_REPLY_TO_CONTROLLER_FLOW_NAME = "GatewayArpReplyRouter";
private static final int ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY = 10000;
private static final Instruction SEND_TO_CONTROLLER_INSTRUCTION;
private SalFlowService flowService;
private final AtomicLong flowCookie = new AtomicLong();
private final ConcurrentMap<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataMap =
- new ConcurrentHashMap<Ipv4Address, ArpResolverMetadata>();
- private final int ARP_WATCH_BROTHERS = 10;
- private final int WAIT_CYCLES = 3;
- private final int PER_CYCLE_WAIT_DURATION = 1000;
- private final int REFRESH_INTERVAL = 10;
+ new ConcurrentHashMap<>();
+ private static final int ARP_WATCH_BROTHERS = 10;
+ private static final int WAIT_CYCLES = 3;
+ private static final int PER_CYCLE_WAIT_DURATION = 1000;
+ private static final int REFRESH_INTERVAL = 10;
private final ListeningExecutorService arpWatcherWall = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ARP_WATCH_BROTHERS));
private final ScheduledExecutorService gatewayMacRefresherPool = Executors.newScheduledThreadPool(1);
private final ScheduledExecutorService refreshRequester = Executors.newSingleThreadScheduledExecutor();
private AtomicBoolean initializationDone = new AtomicBoolean(false);
+ private volatile ConfigurationService configurationService;
+ private volatile NodeCacheManager nodeCacheManager;
static {
ApplyActions applyActions = new ApplyActionsBuilder().setAction(
if (!gatewayToArpMetadataMap.isEmpty()){
for(final Entry<Ipv4Address, ArpResolverMetadata> gatewayToArpMetadataEntry : gatewayToArpMetadataMap.entrySet()){
final Ipv4Address gatewayIp = gatewayToArpMetadataEntry.getKey();
- final ArpResolverMetadata gatewayMetaData = gatewayToArpMetadataEntry.getValue();
+ final ArpResolverMetadata gatewayMetaData =
+ checkAndGetExternalBridgeDpid(
+ resetFlowToRemove(gatewayIp, gatewayToArpMetadataEntry.getValue()));
gatewayMacRefresherPool.schedule(new Runnable(){
@Override
final Node externalNetworkBridge = getExternalBridge(gatewayMetaData.getExternalNetworkBridgeDpid());
if(externalNetworkBridge == null){
LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
- + "is not connected to controller.",gatewayIp.getValue(),gatewayMetaData.getExternalNetworkBridgeDpid() );
+ + "is not connected to controller.",
+ gatewayIp.getValue(),
+ gatewayMetaData.getExternalNetworkBridgeDpid() );
+ } else {
+ LOG.debug("Refresh Gateway Mac for gateway {} using source ip {} and mac {} for ARP request",
+ gatewayIp.getValue(),gatewayMetaData.getArpRequestSourceIp().getValue(),gatewayMetaData.getArpRequestSourceMacAddress().getValue());
+
+ sendGatewayArpRequest(externalNetworkBridge,gatewayIp,gatewayMetaData.getArpRequestSourceIp(), gatewayMetaData.getArpRequestSourceMacAddress());
}
-
- LOG.debug("Refresh Gateway Mac for gateway {} using source ip {} and mac {} for ARP request",
- gatewayIp.getValue(),gatewayMetaData.getArpRequestSourceIp().getValue(),gatewayMetaData.getArpRequestMacAddress().getValue());
-
- sendGatewayArpRequest(externalNetworkBridge,gatewayIp,gatewayMetaData.getArpRequestSourceIp(), gatewayMetaData.getArpRequestMacAddress());
}
}, 1, TimeUnit.SECONDS);
}
* @param sourceIpAddress Source Ip address for the ARP request packet
* @param sourceMacAddress Source Mac address for the ARP request packet
* @param periodicRefresh Enable/Disable periodic refresh of the Gateway Mac address
- * NOTE:Periodic refresh is not supported yet.
- * @param gatewayIp Resolve MAC address of this Gateway Ip
* @return Future object
*/
@Override
- public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
- final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh){
+ public ListenableFuture<MacAddress> resolveMacAddress(
+ final GatewayMacResolverListener gatewayMacResolverListener, final Long externalNetworkBridgeDpid,
+ final Boolean refreshExternalNetworkBridgeDpidIfNeeded,
+ final Ipv4Address gatewayIp, final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress,
+ final Boolean periodicRefresh){
+ Preconditions.checkNotNull(refreshExternalNetworkBridgeDpidIfNeeded);
Preconditions.checkNotNull(sourceIpAddress);
Preconditions.checkNotNull(sourceMacAddress);
Preconditions.checkNotNull(gatewayIp);
});
}
}else{
- gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(
- externalNetworkBridgeDpid, gatewayIp,sourceIpAddress,sourceMacAddress,periodicRefresh));
+ gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(gatewayMacResolverListener,
+ externalNetworkBridgeDpid, refreshExternalNetworkBridgeDpidIfNeeded,
+ gatewayIp, sourceIpAddress, sourceMacAddress, periodicRefresh));
}
final Node externalNetworkBridge = getExternalBridge(externalNetworkBridgeDpid);
- if(externalNetworkBridge == null){
- LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
- + "is not connected to controller.",gatewayIp.getValue(),externalNetworkBridgeDpid );
+ if (externalNetworkBridge == null) {
+ if (!refreshExternalNetworkBridgeDpidIfNeeded) {
+ LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
+ + "is not connected to controller.", gatewayIp.getValue(), externalNetworkBridgeDpid);
+ } else {
+ LOG.debug("MAC address for gateway {} can not be resolved, since dpid was not refreshed yet",
+ gatewayIp.getValue());
+ }
return null;
}
}
private Node getExternalBridge(final Long externalNetworkBridgeDpid){
- final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
+ if (externalNetworkBridgeDpid != null) {
+ final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
+ return getOpenFlowNode(nodeName);
+ }
+ return null;
+ }
+
+ private ArpResolverMetadata checkAndGetExternalBridgeDpid(ArpResolverMetadata gatewayMetaData) {
+ final Long origDpid = gatewayMetaData.getExternalNetworkBridgeDpid();
+
+ // If we are not allowing dpid to change, there is nothing further to do here
+ if (!gatewayMetaData.isRefreshExternalNetworkBridgeDpidIfNeeded()) {
+ return gatewayMetaData;
+ }
+
+ // If current dpid is null, or if mac is not getting resolved, make an attempt to
+ // grab a different dpid, so a different (or updated) external bridge gets used
+ if (origDpid == null || !gatewayMetaData.isGatewayMacAddressResolved()) {
+ Long newDpid = getAnotherExternalBridgeDpid(origDpid);
+ gatewayMetaData.setExternalNetworkBridgeDpid(newDpid);
+ }
+
+ return gatewayMetaData;
+ }
+
+ private Long getAnotherExternalBridgeDpid(final Long unwantedDpid) {
+ LOG.trace("Being asked to find a new dpid. unwantedDpid:{} cachemanager:{} configurationService:{}",
+ unwantedDpid, nodeCacheManager, configurationService);
+
+ if (nodeCacheManager == null) {
+ LOG.error("Unable to find external dpid to use for resolver: no nodeCacheManager");
+ return unwantedDpid;
+ }
+ if (configurationService == null) {
+ LOG.error("Unable to find external dpid to use for resolver: no configurationService");
+ return unwantedDpid;
+ }
+
+ // Pickup another dpid in list that is different than the unwanted one provided and is in the
+ // operational tree. If none can be found, return the provided dpid as a last resort.
+ // NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
+ // the external network is reachable from every br-ex
+ // TODO: Consider other deployment scenario, and think of another solution.
+ List<Long> dpids = nodeCacheManager.getBridgeDpids(configurationService.getExternalBridgeName());
+ Collections.shuffle(dpids);
+ for (Long dpid : dpids) {
+ if (dpid == null || dpid.equals(unwantedDpid) || getExternalBridge(dpid) == null) {
+ continue;
+ }
+
+ LOG.debug("Gateway Mac Resolver Service will use dpid {}", dpid);
+ return dpid;
+ }
- return getOpenFlowNode(nodeName);
+ LOG.warn("Unable to find usable external dpid for resolver. Best choice is still {}", unwantedDpid);
+ return unwantedDpid;
}
private void sendGatewayArpRequest(final Node externalNetworkBridge,final Ipv4Address gatewayIp,
}
LOG.debug("Flow to route ARP Reply to Controller installed successfully : {}", flowIid);
+ ArpResolverMetadata gatewayArpMetadata = gatewayToArpMetadataMap.get(gatewayIp);
+ if (gatewayArpMetadata == null) {
+ LOG.warn("No metadata found for gatewayIp: {}", gatewayIp);
+ return;
+ }
+
//cache metadata
- gatewayToArpMetadataMap.get(gatewayIp).setFlowToRemove(
- new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
+ gatewayArpMetadata.setFlowToRemove(new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
- //Broadcast ARP request packets
+ //get MAC DA for ARP packets
+ MacAddress arpRequestDestMacAddress = gatewayArpMetadata.getArpRequestDestMacAddress();
+
+ //Send ARP request packets
for (NodeConnector egressNc : externalNetworkBridge.getNodeConnector()) {
KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid = nodeIid.child(
NodeConnector.class, new NodeConnectorKey(egressNc.getId()));
ListenableFuture<RpcResult<Void>> futureSendArpResult = arpSender.sendArp(
- senderAddress, gatewayIp, egressNcIid);
+ senderAddress, gatewayIp, arpRequestDestMacAddress, egressNcIid);
Futures.addCallback(futureSendArpResult, logResult(gatewayIp, egressNcIid));
}
}
ArpResolverMetadata arpResolverMetadata = gatewayToArpMetadataMap.get(gatewayIp);
if(arpResolverMetadata != null && arpResolverMetadata.getGatewayMacAddress() != null){
if(!arpResolverMetadata.isPeriodicRefresh()){
+ resetFlowToRemove(gatewayIp, arpResolverMetadata);
return gatewayToArpMetadataMap.remove(gatewayIp).getGatewayMacAddress();
}
return arpResolverMetadata.getGatewayMacAddress();
});
}
- private static @Nullable Ipv4Address getIPv4Addresses(IpAddress ipAddress) {
- if (ipAddress.getIpv4Address() == null) {
- return null;
- }
- return ipAddress.getIpv4Address();
- }
-
private Flow createArpReplyToControllerFlow(final ArpMessageAddress senderAddress, final Ipv4Address ipForRequestedMac) {
checkNotNull(senderAddress);
checkNotNull(ipForRequestedMac);
- FlowBuilder arpFlow = new FlowBuilder().setTableId(TABEL_FOR_ARP_FLOW)
+ FlowBuilder arpFlow = new FlowBuilder().setTableId(Service.CLASSIFIER.getTable())
.setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME)
.setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY)
.setBufferId(OFConstants.OFP_NO_BUFFER)
arpFlow.setMatch(match);
arpFlow.setInstructions(new InstructionsBuilder().setInstruction(
ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build());
- arpFlow.setId(createFlowId(senderAddress, ipForRequestedMac));
+ arpFlow.setId(createFlowId(ipForRequestedMac));
return arpFlow.build();
}
- private FlowId createFlowId(ArpMessageAddress senderAddress, Ipv4Address ipForRequestedMac) {
- StringBuilder sb = new StringBuilder();
- sb.append(ARP_REPLY_TO_CONTROLLER_FLOW_NAME);
- sb.append("|").append(ipForRequestedMac.getValue());
- return new FlowId(sb.toString());
+ private FlowId createFlowId(Ipv4Address ipForRequestedMac) {
+ String flowId = ARP_REPLY_TO_CONTROLLER_FLOW_NAME + "|" + ipForRequestedMac.getValue();
+ return new FlowId(flowId);
}
private static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) {
if(candidateGatewayIp != null){
LOG.debug("Resolved MAC for Gateway Ip {} is {}",gatewayIpAddress.getValue(),gatewayMacAddress.getValue());
candidateGatewayIp.setGatewayMacAddress(gatewayMacAddress);
- flowService.removeFlow(candidateGatewayIp.getFlowToRemove());
+ resetFlowToRemove(gatewayIpAddress, candidateGatewayIp);
}
}
}
}
@Override
- public void setDependencies(Object impl) {}
+ public void setDependencies(Object impl) {
+ if (impl instanceof NodeCacheManager) {
+ nodeCacheManager = (NodeCacheManager) impl;
+ } else if (impl instanceof ConfigurationService) {
+ configurationService = (ConfigurationService) impl;
+ }
+ }
@Override
public void stopPeriodicRefresh(Ipv4Address gatewayIp) {
init();
+ resetFlowToRemove(gatewayIp, null);
gatewayToArpMetadataMap.remove(gatewayIp);
}
+ private ArpResolverMetadata resetFlowToRemove(
+ final Ipv4Address gatewayIp, ArpResolverMetadata gatewayArpMetadata) {
+ checkNotNull(gatewayIp);
+
+ // If gatewayArpMetadata was not provided, look it up
+ if (gatewayArpMetadata == null) {
+ gatewayArpMetadata = gatewayToArpMetadataMap.get(gatewayIp);
+ }
+ if (gatewayArpMetadata != null && gatewayArpMetadata.getFlowToRemove() != null) {
+ LOG.debug("Flow to route ARP Reply to Controller from {} being removed from node {}",
+ gatewayIp, gatewayArpMetadata.getFlowToRemove().getNode());
+ flowService.removeFlow(gatewayArpMetadata.getFlowToRemove());
+ gatewayArpMetadata.setFlowToRemove(null);
+ }
+ return gatewayArpMetadata;
+ }
+
}