mininet.vm.provider "vmware_fusion" do |vf|
vf.vmx["memsize"] = "2048"
end
+ mininet.vm.provider :libvirt do |lv|
+ lv.memory = 2048
+ end
mininet.vm.provision "puppet" do |puppet|
puppet.hiera_config_path = "resources/puppet/hiera.yaml"
puppet.working_directory = "/vagrant/resources/puppet"
control.vm.provider "vmware_fusion" do |vf|
vf.vmx["memsize"] = "4096"
end
+ control.vm.provider :libvirt do |lv|
+ lv.memory = 4096
+ end
control.vm.provision "puppet" do |puppet|
puppet.hiera_config_path = "resources/puppet/hiera.yaml"
puppet.working_directory = "/vagrant/resources/puppet"
compute.vm.provider "vmware_fusion" do |vf|
vf.vmx["memsize"] = "4096"
end
+ compute.vm.provider :libvirt do |lv|
+ lv.memory = 4096
+ end
compute.vm.provision "puppet" do |puppet|
puppet.hiera_config_path = "resources/puppet/hiera.yaml"
puppet.working_directory = "/vagrant/resources/puppet"
ListenableFuture<List<String>> databases = client.getDatabases();
List<String> dbNames = databases.get();
assertNotNull(dbNames);
- if (dbNames.contains(schema)) return true;
- return false;
+ return dbNames.contains(schema);
}
static String testBridgeName = "br_test";
System.out.println("t = " + t);
}
});
- if (updates != null) results.add(updates);
+ if (updates != null) {
+ results.add(updates);
+ }
for (int i = 0; i < 3 ; i++) { //wait 3 seconds to get a result
System.out.println("waiting on monitor response for Bridge Table...");
- if (!results.isEmpty()) break;
+ if (!results.isEmpty()) {
+ break;
+ }
Thread.sleep(1000);
}
Assert.assertTrue(update.getRows().size() > 0);
for (UUID uuid : update.getRows().keySet()) {
Row<GenericTableSchema> aNew = update.getNew(uuid);
- if (!aNew.getColumn(name).getData().equals(testBridgeName)) continue;
+ if (!aNew.getColumn(name).getData().equals(testBridgeName)) {
+ continue;
+ }
if (filter) {
Assert.assertEquals(builder.getColumns().size(), aNew.getColumns().size());
} else {
@After
public void tearDown() throws InterruptedException, ExecutionException {
- if (dbSchema == null) return;
+ if (dbSchema == null) {
+ return;
+ }
TableSchema<GenericTableSchema> bridge = dbSchema.table("Bridge", GenericTableSchema.class);
ColumnSchema<GenericTableSchema, String> name = bridge.column("name", String.class);
GenericTableSchema ovsTable = dbSchema.table("Open_vSwitch", GenericTableSchema.class);
public String getOpenVSwitchTableUUID(Connection connection) throws Exception {
OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
ConcurrentMap<String, Row> row = ovsdbConfigurationService.getRows(node, openVSwitch.getSchema().getName());
- if (row == null || row.size() == 0) return null;
+ if (row == null || row.size() == 0) {
+ return null;
+ }
return (String)row.keySet().toArray()[0];
}
public String getOpenVSwitchTableUUID(Connection connection) throws Exception {
OpenVSwitch openVSwitch = connection.getClient().getTypedRowWrapper(OpenVSwitch.class, null);
ConcurrentMap<UUID, Row<GenericTableSchema>> rows = ovsdbConfigurationService.getRows(node, databaseName, openVSwitch.getSchema().getName());
- if (rows == null || rows.size() == 0) return null;
+ if (rows == null || rows.size() == 0) {
+ return null;
+ }
return rows.keySet().toArray()[0].toString();
}
OvsdbClient client = NodeResource.getOvsdbClient(nodeId, this);
try {
List<String> databases = client.getDatabases().get();
- if (databases == null) return ciDatabaseName;
+ if (databases == null) {
+ return ciDatabaseName;
+ }
for (String csDatabaseName : databases) {
- if (csDatabaseName.equalsIgnoreCase(ciDatabaseName)) return csDatabaseName;
+ if (csDatabaseName.equalsIgnoreCase(ciDatabaseName)) {
+ return csDatabaseName;
+ }
}
return ciDatabaseName;
} catch (Exception e) {
public Response getNodes() throws JsonProcessingException {
OvsdbConnectionService connectionService = (OvsdbConnectionService)ServiceHelper.getGlobalInstance(OvsdbConnectionService.class, this);
List<Node> nodes = connectionService.getNodes();
- if (nodes == null) return Response.noContent().build();
+ if (nodes == null) {
+ return Response.noContent().build();
+ }
List<String> nodeIds = Lists.newArrayList();
for (Node node : nodes) {
private String getBackwardCompatibleTableName(OvsdbClient client, String databaseName, String tableName) {
DatabaseSchema dbSchema = client.getDatabaseSchema(databaseName);
- if (dbSchema == null || tableName == null) return tableName;
+ if (dbSchema == null || tableName == null) {
+ return tableName;
+ }
for (String dbTableName : dbSchema.getTables()) {
- if (dbTableName.equalsIgnoreCase(tableName)) return dbTableName;
+ if (dbTableName.equalsIgnoreCase(tableName)) {
+ return dbTableName;
+ }
}
return tableName;
}
return ciTableName;
}
for (String tableName : tables) {
- if (tableName.equalsIgnoreCase(ciTableName)) return tableName;
+ if (tableName.equalsIgnoreCase(ciTableName)) {
+ return tableName;
+ }
}
return ciTableName;
}
karafUrl = maven()
.groupId("org.opendaylight.ovsdb")
.artifactId("karaf")
- .version("1.2.0-SNAPSHOT")
+ .versionAsInProject()
.type("zip");
return karafUrl;
<ovsdb.utils.servicehelper.version>1.2.0-SNAPSHOT</ovsdb.utils.servicehelper.version>
<powermock.version>1.5.2</powermock.version>
<sonar-jacoco-listeners.version>2.4</sonar-jacoco-listeners.version>
+ <liblldp.version>0.9.1-SNAPSHOT</liblldp.version>
<root.directory>${env.PWD}</root.directory>
<sonar.jacoco.itReportPath>${root.directory}/target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
</properties>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>liblldp</artifactId>
+ <version>${liblldp.version}</version>
+ </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
+
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
import org.opendaylight.ovsdb.openstack.netvirt.api.*;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.OF13Provider;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestratorImpl;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.*;
+import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp.GatewayMacResolverService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
registerService(context, OutboundNatProvider.class.getName(),
outboundNatService, Service.OUTBOUND_NAT);
+ GatewayMacResolverService gatewayMacResolverService = new GatewayMacResolverService();
+ registerService(context, GatewayMacResolver.class.getName(),
+ gatewayMacResolverService, Service.GATEWAY_RESOLVER);
+ getNotificationProviderService().registerNotificationListener(gatewayMacResolverService);
+
+
pipelineOrchestrator.setDependencies(context, null);
outboundNatService.setDependencies(context, null);
egressAclService.setDependencies(context, null);
arpResponderService.setDependencies(context, null);
classifierService.setDependencies(context, null);
of13Provider.setDependencies(context, null);
+ gatewayMacResolverService.setDependencies(context, null);
@SuppressWarnings("unchecked")
ServiceTracker NetworkingProviderManagerTracker = new ServiceTracker(context,
new String[] {AbstractServiceInstance.class.getName(), interfaceClassName},
properties, impl);
}
+
+ private NotificationProviderService getNotificationProviderService(){
+ return this.providerContext.<NotificationProviderService>getSALService(
+ NotificationProviderService.class);
+ }
}
private BundleContext bundleContext = null;
private static DataBroker dataBroker = null;
private ConfigActivator activator;
+ private static ProviderContext providerContext = null;
public NetvirtProvidersProvider(BundleContext bundleContext) {
LOG.info("NetvirtProvidersProvider: bundleContext: {}", bundleContext);
return dataBroker;
}
+ public static ProviderContext getProviderContext() {
+ return providerContext;
+ }
+
@Override
public void close() throws Exception {
activator.stop(bundleContext);
}
@Override
- public void onSessionInitiated(ProviderContext providerContext) {
- dataBroker = providerContext.getSALService(DataBroker.class);
+ public void onSessionInitiated(ProviderContext providerContextRef) {
+ dataBroker = providerContextRef.getSALService(DataBroker.class);
+ providerContext = providerContextRef;
LOG.info("NetvirtProvidersProvider: onSessionInitiated dataBroker: {}", dataBroker);
- this.activator = new ConfigActivator(providerContext);
+ this.activator = new ConfigActivator(providerContextRef);
try {
activator.start(bundleContext);
} catch (Exception e) {
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
+
import java.util.List;
import java.util.concurrent.ExecutionException;
+
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
return null;
}
+ public org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node
+ getOpenFlowNode(String nodeId) {
+
+ ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
+ try {
+ Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node> data =
+ readTx.read(LogicalDatastoreType.OPERATIONAL, createNodePath(createNodeBuilder(nodeId))).get();
+ if (data.isPresent()) {
+ return data.get();
+ }
+ } catch (InterruptedException|ExecutionException e) {
+ logger.error(e.getMessage(), e);
+ }
+
+ logger.debug("Cannot find data for Node " + nodeId);
+ return null;
+ }
+
private Long getDpid(Node node) {
Long dpid = 0L;
dpid = southbound.getDataPathId(node);
@Override
public Service getNextServiceInPipeline(Service service) {
int index = staticPipeline.indexOf(service);
- if (index >= staticPipeline.size() - 1) return null;
+ if (index >= staticPipeline.size() - 1) {
+ return null;
+ }
return staticPipeline.get(index + 1);
}
@Override
public AbstractServiceInstance getServiceInstance(Service service) {
- if (service == null) return null;
+ if (service == null) {
+ return null;
+ }
return serviceRegistry.get(service);
}
public enum Service {
CLASSIFIER ((short) 0, "Classifier"),
+ GATEWAY_RESOLVER((short) 0, "External Network Gateway Resolver"),
DIRECTOR ((short) 10, "Director"),
ARP_RESPONDER ((short) 20, "Distributed ARP Responder"),
INBOUND_NAT ((short) 30, "DNAT for inbound floating-ip traffic"),
if (in.getInstruction() instanceof ApplyActionsCase) {
existingActions = (((ApplyActionsCase) in.getInstruction()).getApplyActions().getAction());
// Only include output actions
- for (Action action : existingActions)
- if (action.getAction() instanceof OutputActionCase)
+ for (Action action : existingActions) {
+ if (action.getAction() instanceof OutputActionCase) {
actionList.add(action);
+ }
+ }
}
}
/* Create output action for this port*/
// Match Tunnel-ID, VIP, and Reg0==0
if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
- lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
- else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
- else
+ } else {
return; //Should not get here. TODO: Other types
+ }
MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(lbConfig.getVip()));
MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, FIRST_PASS_REGA_MATCH_VALUE));
// Match Tunnel-ID, VIP, Reg0==1 and Reg1==Index of member
if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
- lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
- else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
- else
+ } else {
return; //Should not get here. TODO: Other types
+ }
MatchUtils.createDstL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(vip));
MatchUtils.addNxRegMatch(matchBuilder, new MatchUtils.RegMatch(REG_FIELD_A, SECOND_PASS_REGA_MATCH_VALUE),
// Match Tunnel-ID, MemberIP, and Protocol/Port
if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VXLAN) ||
- lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE))
+ lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_GRE)) {
MatchUtils.createTunnelIDMatch(matchBuilder, new BigInteger(lbConfig.getProviderSegmentationId()));
- else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN))
+ } else if (lbConfig.getProviderNetworkType().equalsIgnoreCase(NetworkHandler.NETWORK_TYPE_VLAN)) {
MatchUtils.createVlanIdMatch(matchBuilder, new VlanId(Integer.valueOf(lbConfig.getProviderSegmentationId())), true);
- else
+ } else {
return; //Should not get here. TODO: Other types
+ }
MatchUtils.createSrcL3IPv4Match(matchBuilder, MatchUtils.iPv4PrefixFromIPv4Address(member.getIP()));
MatchUtils.createSetSrcTcpMatch(matchBuilder, new PortNumber(member.getPort()));
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import io.netty.buffer.Unpooled;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.Packet;
+import org.opendaylight.controller.liblldp.PacketException;
+
+/**
+ * Represents ARP packet. Contains methods ({@link #setSHAFieldCoordinate(Pair)}
+ * {@link #setSPAFieldCoordinate(Pair)} {@link #setTHAFieldCoordinate(Pair)}
+ * {@link #setTPAFieldCoordinate(Pair)}) for customization of ARP.
+ * Arp by default contain fields for IPv4 as protocol address and MAC as hardware address.
+ */
+public class Arp extends Packet {
+
+ private static final String HTYPE = "htype";
+ private static final String PTYPE = "ptype";
+ private static final String HLEN = "hlen";
+ private static final String PLEN = "plen";
+ private static final String OPERATION = "operation";
+ private static final String SHA = "sha";
+ private static final String SPA = "spa";
+ private static final String THA = "tha";
+ private static final String TPA = "tpa";
+
+ private static final int ARP_FIELDS_COUNT = 9;
+ private static final int ETHERNET_HW_TYPE = 1;
+ private final Map<String, Pair<Integer, Integer>> ARP_FIELD_COORDINATES = new LinkedHashMap<String, Pair<Integer, Integer>>() {
+
+ private static final long serialVersionUID = 1L;
+
+ {
+ put(HTYPE, ImmutablePair.of(0, 16));
+ put(PTYPE, ImmutablePair.of(16, 16));
+ put(HLEN, ImmutablePair.of(32, 8));
+ put(PLEN, ImmutablePair.of(40, 8));
+ put(OPERATION, ImmutablePair.of(48, 16));
+ put(SHA, ImmutablePair.of(64, 48));
+ put(SPA, ImmutablePair.of(112, 32));
+ put(THA, ImmutablePair.of(144, 48));
+ put(TPA, ImmutablePair.of(192, 32));
+ }
+ };
+
+ public Arp() {
+ payload = null;
+ hdrFieldsMap = new HashMap<String, byte[]>(ARP_FIELDS_COUNT);
+ setHardwareLength((short) 6); // MAC address length
+ setProtocolLength((short) 4); // IPv4 address length
+ setHardwareType(ETHERNET_HW_TYPE);
+ setProtocolType(EtherTypes.IPv4.intValue());
+ hdrFieldCoordMap = ARP_FIELD_COORDINATES;
+ }
+
+ public Pair<Integer, Integer> setSHAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+ checkNotNullPair(bitOffsetAndBitLength);
+ return ARP_FIELD_COORDINATES.put(SHA, bitOffsetAndBitLength);
+ }
+
+ public Pair<Integer, Integer> setSPAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+ checkNotNullPair(bitOffsetAndBitLength);
+ return ARP_FIELD_COORDINATES.put(SPA, bitOffsetAndBitLength);
+ }
+
+ public Pair<Integer, Integer> setTHAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+ checkNotNullPair(bitOffsetAndBitLength);
+ return ARP_FIELD_COORDINATES.put(THA, bitOffsetAndBitLength);
+ }
+
+ public Pair<Integer, Integer> setTPAFieldCoordinate(Pair<Integer, Integer> bitOffsetAndBitLength) {
+ checkNotNullPair(bitOffsetAndBitLength);
+ return ARP_FIELD_COORDINATES.put(TPA, bitOffsetAndBitLength);
+ }
+
+ private void checkNotNullPair(Pair<Integer, Integer> pair) {
+ checkNotNull(pair);
+ checkNotNull(pair.getLeft());
+ checkNotNull(pair.getRight());
+ }
+
+ @Override
+ public Packet deserialize(byte[] data, int bitOffset, int size) throws PacketException {
+ return super.deserialize(data, bitOffset, size);
+ }
+
+ @Override
+ public byte[] serialize() throws PacketException {
+ return super.serialize();
+ }
+
+ @Override
+ public int getfieldnumBits(String fieldName) {
+ if (fieldName.equals(SHA) || fieldName.equals(THA)) {
+ return getHardwareLength() * NetUtils.NumBitsInAByte;
+ } else if (fieldName.equals(SPA) || fieldName.equals(TPA)) {
+ return getProtocolLength() * NetUtils.NumBitsInAByte;
+ }
+ return hdrFieldCoordMap.get(fieldName).getRight();
+ }
+
+ public Arp setHardwareType(int value) {
+ hdrFieldsMap.put(HTYPE, Unpooled.copyShort(value).array());
+ return this;
+ }
+
+ public Arp setProtocolType(int value) {
+ hdrFieldsMap.put(PTYPE, Unpooled.copyShort(value).array());
+ return this;
+ }
+
+ /**
+ * @param value hardware length in Bytes
+ */
+ public Arp setHardwareLength(short value) {
+ hdrFieldsMap.put(HLEN, Unpooled.buffer(1).writeByte(value).array());
+ return this;
+ }
+
+ /**
+ * @param value protocol length in Bytes
+ */
+ public Arp setProtocolLength(short value) {
+ hdrFieldsMap.put(PLEN, Unpooled.buffer(1).writeByte(value).array());
+ return this;
+ }
+
+ public Arp setOperation(int value) {
+ hdrFieldsMap.put(OPERATION, Unpooled.copyShort(value).array());
+ return this;
+ }
+
+ public Arp setSenderHardwareAddress(byte[] value) {
+ hdrFieldsMap.put(SHA, value);
+ return this;
+ }
+
+ public Arp setSenderProtocolAddress(byte[] value) {
+ hdrFieldsMap.put(SPA, value);
+ return this;
+ }
+
+ public Arp setTargetHardwareAddress(byte[] value) {
+ hdrFieldsMap.put(THA, value);
+ return this;
+ }
+
+ public Arp setTargetProtocolAddress(byte[] value) {
+ hdrFieldsMap.put(TPA, value);
+ return this;
+ }
+
+ public int getHardwareType() {
+ byte[] htype = hdrFieldsMap.get(HTYPE);
+ return Unpooled.wrappedBuffer(htype).readUnsignedShort();
+ }
+
+ public int getProtocolType() {
+ byte[] ptype = hdrFieldsMap.get(PTYPE);
+ return Unpooled.wrappedBuffer(ptype).readUnsignedShort();
+ }
+
+ public short getHardwareLength() {
+ byte[] hlen = hdrFieldsMap.get(HLEN);
+ return Unpooled.wrappedBuffer(hlen).readUnsignedByte();
+ }
+
+ public short getProtocolLength() {
+ byte[] plen = hdrFieldsMap.get(PLEN);
+ return Unpooled.wrappedBuffer(plen).readUnsignedByte();
+ }
+
+ public int getOperation() {
+ byte[] operation = hdrFieldsMap.get(OPERATION);
+ return Unpooled.wrappedBuffer(operation).readUnsignedShort();
+ }
+
+ public byte[] getSenderHardwareAddress() {
+ return hdrFieldsMap.get(SHA);
+ }
+
+ public byte[] getSenderProtocolAddress() {
+ return hdrFieldsMap.get(SPA);
+ }
+
+ public byte[] getTargetHardwareAddress() {
+ return hdrFieldsMap.get(THA);
+ }
+
+ public byte[] getTargetProtocolAddress() {
+ return hdrFieldsMap.get(TPA);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+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.inet.types.rev100924.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+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.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpTargetHardwareAddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
+
+/**
+ * Contains methods creating flow part for ARP flow.
+ */
+public class ArpFlowFactory {
+
+ private static final String HOST_MASK = "/32";
+
+ /**
+ * Creates {@link EthernetMatch} containing ARP ether-type and the given destination MAC address
+ */
+ public static EthernetMatch createEthernetMatch(MacAddress destinationMacAddress) {
+ return new EthernetMatchBuilder().setEthernetType(
+ new EthernetTypeBuilder().setType(new EtherType(Long.valueOf(EtherTypes.ARP.intValue()))).build())
+ .setEthernetDestination(new EthernetDestinationBuilder().setAddress(destinationMacAddress).build())
+ .build();
+ }
+
+ /**
+ * Creates {@link ArpMatch} containing Reply ARP operation, THA and TPA for the given target
+ * address and SPA for the given sender protocol address
+ */
+ public static ArpMatch createArpMatch(ArpMessageAddress targetAddress, Ipv4Address senderProtocolAddress) {
+ return new ArpMatchBuilder().setArpOp(ArpOperation.REPLY.intValue())
+ .setArpTargetHardwareAddress(
+ new ArpTargetHardwareAddressBuilder().setAddress(targetAddress.getHardwareAddress()).build())
+ .setArpTargetTransportAddress(new Ipv4Prefix(targetAddress.getProtocolAddress().getValue() + HOST_MASK))
+ .setArpSourceTransportAddress(new Ipv4Prefix(senderProtocolAddress.getValue() + HOST_MASK))
+ .build();
+ }
+
+ /**
+ * Creates {@link Action} representing output to the controller
+ *
+ * @param order the order for the action
+ */
+ public static Action createSendToControllerAction(int order) {
+ return new ActionBuilder().setOrder(order)
+ .setKey(new ActionKey(order))
+ .setAction(
+ new OutputActionCaseBuilder().setOutputAction(
+ new OutputActionBuilder().setMaxLength(0xffff)
+ .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString()))
+ .build()).build())
+ .build();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.concurrent.Immutable;
+
+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;
+
+/**
+ * Represents ARP fields where protocol address is IPv4 address and hardware address is MAC address.
+ */
+@Immutable
+public class ArpMessageAddress {
+
+ private final MacAddress hwAddress;
+ private final Ipv4Address protocolAddress;
+
+ public ArpMessageAddress(MacAddress hwAddress, Ipv4Address protocolAddress) {
+ this.hwAddress = checkNotNull(hwAddress);
+ this.protocolAddress = checkNotNull(protocolAddress);
+ }
+
+ public MacAddress getHardwareAddress() {
+ return hwAddress;
+ }
+
+ public Ipv4Address getProtocolAddress() {
+ return protocolAddress;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import javax.annotation.Nullable;
+
+public enum ArpOperation {
+
+ REQUEST(1), REPLY(2);
+
+ private final int intOperation;
+
+ private ArpOperation(int operationNumber) {
+ this.intOperation = operationNumber;
+ }
+
+ public int intValue() {
+ return intOperation;
+ }
+
+ public static @Nullable ArpOperation loadFromInt(int intOperation) {
+ for (ArpOperation operation : ArpOperation.values()) {
+ if (operation.intOperation == intOperation) {
+ return operation;
+ }
+ }
+ return null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+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.service.rev130819.RemoveFlowInput;
+
+/**
+*
+* @author Anil Vishnoi (avishnoi@Brocade.com)
+*
+*/
+
+public final class ArpResolverMetadata {
+
+ private RemoveFlowInput flowToRemove;
+ private Ipv4Address gatewayIpAddress;
+ private MacAddress gatewayMacAddress;
+ private boolean periodicRefresh;
+
+ public ArpResolverMetadata(){
+
+ }
+ public ArpResolverMetadata(RemoveFlowInput flowToRemove, Ipv4Address gatewayIpAddress, boolean periodicRefresh){
+ this.flowToRemove = flowToRemove;
+ this.gatewayIpAddress = gatewayIpAddress;
+ this.periodicRefresh = periodicRefresh;
+ }
+
+ public ArpResolverMetadata(RemoveFlowInput flowToRemove, Ipv4Address gatewayIpAddress, MacAddress gatewayMacAddress, boolean periodicRefresh){
+ this.flowToRemove = flowToRemove;
+ this.gatewayIpAddress = gatewayIpAddress;
+ this.gatewayMacAddress = gatewayMacAddress;
+ this.periodicRefresh = periodicRefresh;
+ }
+
+ public RemoveFlowInput getFlowToRemove() {
+ return flowToRemove;
+ }
+ public boolean isPeriodicRefresh() {
+ return periodicRefresh;
+ }
+ public void setPeriodicRefresh(boolean periodicRefresh) {
+ this.periodicRefresh = periodicRefresh;
+ }
+ public void setFlowToRemove(RemoveFlowInput flowToRemove) {
+ this.flowToRemove = flowToRemove;
+ }
+ public Ipv4Address getGatewayIpAddress() {
+ return gatewayIpAddress;
+ }
+ public void setGatewayIpAddress(Ipv4Address gatewayIpAddress) {
+ this.gatewayIpAddress = gatewayIpAddress;
+ }
+ public MacAddress getGatewayMacAddress() {
+ return gatewayMacAddress;
+ }
+ public void setGatewayMacAddress(MacAddress gatewayMacAddress) {
+ this.gatewayMacAddress = gatewayMacAddress;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+
+public class ArpResolverUtils {
+
+ static {
+ Ethernet.etherTypeClassMap.put(EtherTypes.ARP.shortValue(), Arp.class);
+ }
+
+ /**
+ * Tries to deserialize received packet as ARP packet with IPv4 protocol address and MAC
+ * hardware address.
+ *
+ * @param potentialArp the packet for deserialization
+ * @return ARP packet if received packet is ARP and deserialization was successful
+ * @throws PacketException if packet is not ARP or deserialization was not successful
+ */
+ public static Arp getArpFrom(PacketReceived potentialArp) throws PacketException {
+ byte[] payload = potentialArp.getPayload();
+ Ethernet ethPkt = new Ethernet();
+ ethPkt.deserialize(payload, 0, payload.length * NetUtils.NumBitsInAByte);
+ if (ethPkt.getPayload() instanceof Arp) {
+ return (Arp) ethPkt.getPayload();
+ }
+ throw new PacketException("Packet is not ARP: " + potentialArp);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.controller.liblldp.PacketException;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Uses packet-out for sending ARP Requests.
+ */
+public class ArpSender {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ArpSender.class);
+
+ private static final String OFPP_ALL = "0xfffffffc";
+ private final PacketProcessingService packetProcessingService;
+
+ public ArpSender(PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = checkNotNull(packetProcessingService);
+ }
+
+ /**
+ * Sends ARP Request as packet-out from all openflow physical ports on the given node.
+ *
+ * @param senderAddress the addresses used in sender part of ARP packet
+ * @param tpa the target protocol address, in this case IPv4 address for which MAC should be
+ * discovered
+ * @param nodeIid the path to node from where the ARP packet will be flooded
+ * @return future result about success of packet-out
+ */
+ public ListenableFuture<RpcResult<Void>> floodArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
+ InstanceIdentifier<Node> nodeIid) {
+ checkNotNull(senderAddress);
+ checkNotNull(tpa);
+ checkNotNull(nodeIid);
+ // node connector representing all physical ports on node
+ NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(createNodeConnectorId(OFPP_ALL,
+ nodeIid.firstKeyOf(Node.class, NodeKey.class).getId()));
+ InstanceIdentifier<NodeConnector> egressNc = nodeIid.child(NodeConnector.class, nodeConnectorKey);
+ return sendArp(senderAddress, tpa, egressNc);
+ }
+
+ private NodeConnectorId createNodeConnectorId(String connectorId, NodeId nodeId) {
+ StringBuilder stringId = new StringBuilder(nodeId.getValue()).append(":").append(connectorId);
+ return new NodeConnectorId(stringId.toString());
+ }
+
+ /**
+ * Sends ARP Request as packet-out from the given port (node connector).
+ *
+ * @param senderAddress the addresses used in sender part of ARP packet
+ * @param tpa the target protocol address, in this case IPv4 address for which MAC should be
+ * discovered
+ * @param egressNc the path to node connector from where the ARP packet will be sent
+ * @return future result about success of packet-out
+ */
+ public ListenableFuture<RpcResult<Void>> sendArp(ArpMessageAddress senderAddress, Ipv4Address tpa,
+ InstanceIdentifier<NodeConnector> egressNc) {
+ checkNotNull(senderAddress);
+ checkNotNull(tpa);
+ checkNotNull(egressNc);
+ final Ethernet arpFrame = createArpFrame(senderAddress, tpa);
+ byte[] arpFrameAsBytes;
+ try {
+ arpFrameAsBytes = arpFrame.serialize();
+ } catch (PacketException e) {
+ LOG.warn("Serializition of ARP packet is not successful.", e);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("ARP packet: {}", ArpUtils.getArpFrameToStringFormat(arpFrame));
+ }
+ return Futures.immediateFailedFuture(e);
+ }
+ // Generate packet with destination switch and port
+ TransmitPacketInput packet = new TransmitPacketInputBuilder().setEgress(new NodeConnectorRef(egressNc))
+ .setNode(new NodeRef(egressNc.firstIdentifierOf(Node.class)))
+ .setPayload(arpFrameAsBytes)
+ .build();
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Sending ARP REQUEST \n{}", ArpUtils.getArpFrameToStringFormat(arpFrame));
+ }
+ Future<RpcResult<Void>> futureTransmitPacketResult = packetProcessingService.transmitPacket(packet);
+ return JdkFutureAdapters.listenInPoolThread(futureTransmitPacketResult);
+ }
+
+ private Ethernet createArpFrame(ArpMessageAddress senderAddress, Ipv4Address tpa) {
+ byte[] senderMac = ArpUtils.macToBytes(senderAddress.getHardwareAddress());
+ byte[] senderIp = ArpUtils.ipToBytes(senderAddress.getProtocolAddress());
+ byte[] targetMac = NetUtils.getBroadcastMACAddr();
+ byte[] targetIp = ArpUtils.ipToBytes(tpa);
+ Ethernet arpFrame = new Ethernet().setSourceMACAddress(senderMac)
+ .setDestinationMACAddress(targetMac)
+ .setEtherType(EtherTypes.ARP.shortValue());
+ Arp arp = new Arp().setOperation(ArpOperation.REQUEST.intValue())
+ .setSenderHardwareAddress(senderMac)
+ .setSenderProtocolAddress(senderIp)
+ .setTargetHardwareAddress(targetMac)
+ .setTargetProtocolAddress(targetIp);
+ arpFrame.setPayload(arp);
+ return arpFrame;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.liblldp.EtherTypes;
+import org.opendaylight.controller.liblldp.Ethernet;
+import org.opendaylight.controller.liblldp.HexEncode;
+import org.opendaylight.controller.liblldp.Packet;
+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 com.google.common.net.InetAddresses;
+
+public class ArpUtils {
+
+ private ArpUtils() {
+ throw new UnsupportedOperationException("Cannot create an instance.");
+ }
+
+ /**
+ * Returns Ethernet and ARP in readable string format
+ */
+ public static String getArpFrameToStringFormat(Ethernet eth) {
+ String ethernetString = "Ethernet [getEtherType()="
+ + EtherTypes.loadFromString(String.valueOf(eth.getEtherType())) + ", getSourceMACAddress()="
+ + HexEncode.bytesToHexStringFormat(eth.getSourceMACAddress()) + ", getDestinationMACAddress()="
+ + HexEncode.bytesToHexStringFormat(eth.getDestinationMACAddress()) + "]\n";
+ Packet potentialArp = eth.getPayload();
+ String arpString = null;
+ if (potentialArp instanceof Arp) {
+ Arp arp = (Arp) potentialArp;
+ arpString = ArpUtils.getArpToStringFormat(arp);
+ } else {
+ arpString = "ARP was not found in Ethernet frame.";
+ }
+ return ethernetString.concat(arpString);
+ }
+
+ /**
+ * Returns ARP in readable string format
+ */
+ public static String getArpToStringFormat(Arp arp) {
+ try {
+ return "Arp [getHardwareType()=" + arp.getHardwareType() + ", getProtocolType()=" + arp.getProtocolType()
+ + ", getHardwareLength()=" + arp.getHardwareLength() + ", getProtocolLength()="
+ + arp.getProtocolLength() + ", getOperation()=" + ArpOperation.loadFromInt(arp.getOperation())
+ + ", getSenderHardwareAddress()="
+ + HexEncode.bytesToHexStringFormat(arp.getSenderHardwareAddress())
+ + ", getSenderProtocolAddress()="
+ + InetAddress.getByAddress(arp.getSenderProtocolAddress()).getHostAddress()
+ + ", getTargetHardwareAddress()="
+ + HexEncode.bytesToHexStringFormat(arp.getTargetHardwareAddress())
+ + ", getTargetProtocolAddress()="
+ + InetAddress.getByAddress(arp.getTargetProtocolAddress()).getHostAddress() + "]\n";
+ } catch (UnknownHostException e1) {
+ return "Error during parsing Arp " + arp;
+ }
+ }
+
+ public static byte[] macToBytes(MacAddress mac) {
+ return HexEncode.bytesFromHexString(mac.getValue());
+ }
+
+ public static @Nullable MacAddress bytesToMac(byte[] macBytes) {
+ String mac = HexEncode.bytesToHexStringFormat(macBytes);
+ if (!"null".equals(mac)) {
+ return new MacAddress(mac);
+ }
+ return null;
+ }
+
+ public static byte[] ipToBytes(Ipv4Address ip) {
+ return InetAddresses.forString(ip.getValue()).getAddress();
+ }
+
+ public static @Nullable Ipv4Address bytesToIp(byte[] ipv4AsBytes) {
+ try {
+ return new Ipv4Address(InetAddress.getByAddress(ipv4AsBytes).getHostAddress());
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+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.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.Nullable;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+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.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+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;
+
+/**
+ *
+ * @author Anil Vishnoi (avishnoi@Brocade.com)
+ *
+ */
+public class GatewayMacResolverService extends AbstractServiceInstance
+ 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 ArpSender arpSender;
+ private SalFlowService flowService;
+ private final AtomicLong flowCookie = new AtomicLong();
+ private final ConcurrentHashMap<Ipv4Address, ArpResolverMetadata> arpRemoveFlowInputAndL3EpKeyById =
+ 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 ListeningExecutorService arpWatcherWall = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(ARP_WATCH_BROTHERS));
+ private AtomicBoolean initializationDone = new AtomicBoolean(false);
+
+ static {
+ ApplyActions applyActions = new ApplyActionsBuilder().setAction(
+ ImmutableList.of(ArpFlowFactory.createSendToControllerAction(0))).build();
+ SEND_TO_CONTROLLER_INSTRUCTION = new InstructionBuilder().setOrder(0)
+ .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyActions).build())
+ .build();
+ }
+
+ public GatewayMacResolverService(){
+ super(Service.GATEWAY_RESOLVER);
+ }
+
+ public GatewayMacResolverService(Service service){
+ super(service);
+ }
+
+ private void init(){
+ if(!initializationDone.get()){
+ initializationDone.set(true);
+ ProviderContext providerContext = NetvirtProvidersProvider.getProviderContext();
+ checkNotNull(providerContext);
+ PacketProcessingService packetProcessingService = providerContext.getRpcService(PacketProcessingService.class);
+ if (packetProcessingService != null) {
+ LOG.debug("{} was found.", PacketProcessingService.class.getSimpleName());
+ this.arpSender = new ArpSender(packetProcessingService);
+ } else {
+ LOG.error("Missing service {}", PacketProcessingService.class.getSimpleName());
+ this.arpSender = null;
+ }
+ flowService = providerContext.getRpcService(SalFlowService.class);
+ }
+ }
+ /**
+ * Method do following actions:
+ * 1. Install flow to direct ARP response packet to controller
+ * 2. Send ARP request packet out on all port of the given External network bridge.
+ * 3. Cache the flow that need to be removed once ARP resolution is done.
+ * 4. Return listenable future so that user can add callback to get the MacAddress
+ * @param externalNetworkBridgeDpid Broadcast ARP request packet on this bridge
+ * @param gatewayIp IP address for which MAC need to be resolved
+ * @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<MacAddress> Future object
+ */
+ @Override
+ public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
+ final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh){
+ Preconditions.checkNotNull(sourceIpAddress);
+ Preconditions.checkNotNull(sourceMacAddress);
+ Preconditions.checkNotNull(gatewayIp);
+
+ LOG.info("Trigger Mac resolution for gateway {}, using source ip {} and mac {}",
+ gatewayIp.getValue(),sourceIpAddress.getValue(),sourceMacAddress.getValue());
+
+ init();
+ if(arpRemoveFlowInputAndL3EpKeyById.containsKey(gatewayIp)){
+ if(arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).getGatewayMacAddress() != null){
+ return arpWatcherWall.submit(new Callable<MacAddress>(){
+
+ @Override
+ public MacAddress call() throws Exception {
+ return arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).getGatewayMacAddress();
+ }
+ });
+ }
+ }else{
+ arpRemoveFlowInputAndL3EpKeyById.put(gatewayIp,
+ new ArpResolverMetadata(null, gatewayIp,periodicRefresh));
+ }
+
+ final ArpMessageAddress senderAddress = new ArpMessageAddress(sourceMacAddress,sourceIpAddress);
+
+ final String nodeName = OPENFLOW + externalNetworkBridgeDpid;
+
+ final Node externalNetworkBridge = getOpenFlowNode(nodeName);
+ if(externalNetworkBridge == null){
+ LOG.error("MAC address for gateway {} can not be resolved, because external bridge {} "
+ + "is not connected to controller.",gatewayIp.getValue(),externalNetworkBridgeDpid );
+ return null;
+ }
+ //Build arp reply router flow
+ final Flow arpReplyToControllerFlow = createArpReplyToControllerFlow(senderAddress, gatewayIp);
+
+
+ final InstanceIdentifier<Node> nodeIid = InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, externalNetworkBridge.getKey())
+ .build();
+ final InstanceIdentifier<Flow> flowIid = createFlowIid(arpReplyToControllerFlow, nodeIid);
+ final NodeRef nodeRef = new NodeRef(nodeIid);
+
+ //Install flow
+ Future<RpcResult<AddFlowOutput>> addFlowResult = flowService.addFlow(new AddFlowInputBuilder(
+ arpReplyToControllerFlow).setFlowRef(new FlowRef(flowIid)).setNode(nodeRef).build());
+ //wait for flow installation
+ Futures.addCallback(JdkFutureAdapters.listenInPoolThread(addFlowResult),
+ new FutureCallback<RpcResult<AddFlowOutput>>() {
+
+ @Override
+ public void onSuccess(RpcResult<AddFlowOutput> result) {
+ if (!result.isSuccessful()) {
+ LOG.warn("Flow to route ARP Reply to Controller is not installed successfully : {} \nErrors: {}", flowIid,result.getErrors());
+ return;
+ }
+ LOG.debug("Flow to route ARP Reply to Controller installed successfully : {}", flowIid);
+
+ //cache metadata
+ arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp).setFlowToRemove(
+ new RemoveFlowInputBuilder(arpReplyToControllerFlow).setNode(nodeRef).build());
+
+ //Broadcast 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);
+ Futures.addCallback(futureSendArpResult, logResult(gatewayIp, egressNcIid));
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.warn("ARP Reply to Controller flow was not created: {}", flowIid, t);
+ }
+ }
+ );
+ //Wait for MacAddress population in cache
+ return waitForMacAddress(gatewayIp);
+ }
+
+ private ListenableFuture<MacAddress> waitForMacAddress(final Ipv4Address gatewayIp){
+
+ return arpWatcherWall.submit(new Callable<MacAddress>(){
+
+ @Override
+ public MacAddress call() throws Exception {
+ for(int cycle = 0;cycle < WAIT_CYCLES;cycle++){
+ //Sleep before checking mac address, so meanwhile ARP request packets
+ // will be broadcasted on the bridge.
+ Thread.sleep(PER_CYCLE_WAIT_DURATION);
+ ArpResolverMetadata arpResolverMetadata = arpRemoveFlowInputAndL3EpKeyById.get(gatewayIp);
+ if(arpResolverMetadata != null && arpResolverMetadata.getGatewayMacAddress() != null){
+ if(!arpResolverMetadata.isPeriodicRefresh()){
+ return arpRemoveFlowInputAndL3EpKeyById.remove(gatewayIp).getGatewayMacAddress();
+ }
+ return arpResolverMetadata.getGatewayMacAddress();
+ }
+ }
+ return null;
+ }
+ });
+ }
+ 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)
+ .setFlowName(ARP_REPLY_TO_CONTROLLER_FLOW_NAME)
+ .setPriority(ARP_REPLY_TO_CONTROLLER_FLOW_PRIORITY)
+ .setBufferId(OFConstants.OFP_NO_BUFFER)
+ .setIdleTimeout(0)
+ .setHardTimeout(0)
+ .setCookie(new FlowCookie(BigInteger.valueOf(flowCookie.incrementAndGet())))
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ EthernetMatch ethernetMatch = ArpFlowFactory.createEthernetMatch(senderAddress.getHardwareAddress());
+ ArpMatch arpMatch = ArpFlowFactory.createArpMatch(senderAddress, ipForRequestedMac);
+ Match match = new MatchBuilder().setEthernetMatch(ethernetMatch).setLayer3Match(arpMatch).build();
+ arpFlow.setMatch(match);
+ arpFlow.setInstructions(new InstructionsBuilder().setInstruction(
+ ImmutableList.of(SEND_TO_CONTROLLER_INSTRUCTION)).build());
+ arpFlow.setId(createFlowId(senderAddress, 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 static InstanceIdentifier<Flow> createFlowIid(Flow flow, InstanceIdentifier<Node> nodeIid) {
+ return nodeIid.builder()
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(flow.getTableId()))
+ .child(Flow.class, new FlowKey(flow.getId()))
+ .build();
+ }
+
+ private FutureCallback<RpcResult<Void>> logResult(final Ipv4Address tpa,
+ final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> egressNcIid) {
+ return new FutureCallback<RpcResult<Void>>() {
+
+ @Override
+ public void onSuccess(RpcResult<Void> result) {
+ LOG.debug("ARP Request for IP {} was sent from {}.", tpa.getValue(), egressNcIid);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.warn("ARP Request for IP {} was NOT sent from {}.", tpa.getValue(), egressNcIid);
+ }
+ };
+ }
+
+ @Override
+ public void onPacketReceived(PacketReceived potentialArp) {
+ Arp arp = null;
+ try {
+ arp = ArpResolverUtils.getArpFrom(potentialArp);
+ } catch (Exception e) {
+ LOG.trace(
+ "Failed to decode potential ARP packet. This could occur when other than ARP packet was received.",
+ e);
+ return;
+ }
+ if (arp.getOperation() != ArpOperation.REPLY.intValue()) {
+ LOG.trace("Packet is not ARP REPLY packet.");
+ return;
+ }
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("ARP REPLY received - {}", ArpUtils.getArpToStringFormat(arp));
+ }
+ NodeKey nodeKey = potentialArp.getIngress().getValue().firstKeyOf(Node.class, NodeKey.class);
+ if (nodeKey == null) {
+ LOG.info("Unknown source node of ARP packet: {}", potentialArp);
+ return;
+ }
+ Ipv4Address gatewayIpAddress = ArpUtils.bytesToIp(arp.getSenderProtocolAddress());
+ MacAddress gatewayMacAddress = ArpUtils.bytesToMac(arp.getSenderHardwareAddress());
+ ArpResolverMetadata removeFlowInputAndL3EpKey = arpRemoveFlowInputAndL3EpKeyById.get(gatewayIpAddress);
+ if(removeFlowInputAndL3EpKey != null){
+ removeFlowInputAndL3EpKey.setGatewayMacAddress(gatewayMacAddress);
+ flowService.removeFlow(removeFlowInputAndL3EpKey.getFlowToRemove());
+ }
+ }
+
+ @Override
+ public void setDependencies(BundleContext bundleContext,
+ ServiceReference serviceReference) {
+ super.setDependencies(bundleContext.getServiceReference(GatewayMacResolver.class.getName()), this);
+
+ }
+
+ @Override
+ public void setDependencies(Object impl) {}
+
+ @Override
+ public void stopPeriodicReferesh(Ipv4Address gatewayIp) {
+ init();
+ arpRemoveFlowInputAndL3EpKeyById.remove(gatewayIp);
+ }
+
+}
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
AbstractEvent other = (AbstractEvent) obj;
if (handlerType == null) {
- if (other.handlerType != null)
+ if (other.handlerType != null) {
return false;
- } else if (!handlerType.equals(other.handlerType))
+ }
+ } else if (!handlerType.equals(other.handlerType)) {
return false;
+ }
if (action == null) {
- if (other.action != null)
+ if (other.action != null) {
return false;
- } else if (!action.equals(other.action))
+ }
+ } else if (!action.equals(other.action)) {
return false;
+ }
return true;
}
}
private ServiceTracker outboundNatProviderTracker;
private ServiceTracker routingProviderTracker;
private ServiceTracker l3ForwardingProviderTracker;
+ private ServiceTracker gatewayMacResolverProviderTracker;
public ConfigActivator(ProviderContext providerContext) {
this.providerContext = providerContext;
};
l3ForwardingProviderTracker.open();
this.l3ForwardingProviderTracker = l3ForwardingProviderTracker;
+
+ @SuppressWarnings("unchecked")
+ ServiceTracker gatewayMacResolverProviderTracker = new ServiceTracker(context,
+ GatewayMacResolver.class, null) {
+ @Override
+ public Object addingService(ServiceReference reference) {
+ LOG.info("addingService GatwayMacResolverProvider");
+ GatewayMacResolver service =
+ (GatewayMacResolver) context.getService(reference);
+ if (service != null) {
+ neutronL3Adapter.setDependencies(service);
+ }
+ return service;
+ }
+ };
+ gatewayMacResolverProviderTracker.open();
+ this.gatewayMacResolverProviderTracker = gatewayMacResolverProviderTracker;
}
+
@Override
public void stop(BundleContext context) throws Exception {
LOG.info("ConfigActivator stop");
}
}
- if (lbConfig.getMembers().size() > 0)
+ if (lbConfig.getMembers().size() > 0) {
lbConfigList.add(lbConfig);
+ }
}
return lbConfigList;
* @return MAC address registered with that IP address
*/
public static String getMacAddress(INeutronPortCRUD neutronPortsCache, String subnetID, String ipAddr) {
- if (ipAddr == null || subnetID == null)
+ if (ipAddr == null || subnetID == null) {
return null;
+ }
List<Neutron_IPs> fixedIPs;
Iterator<Neutron_IPs> fixedIPIterator;
fixedIPIterator = fixedIPs.iterator();
while (fixedIPIterator.hasNext()) {
ip = fixedIPIterator.next();
- if (ip.getIpAddress().equals(ipAddr) && ip.getSubnetUUID().equals(subnetID))
+ if (ip.getIpAddress().equals(ipAddr) && ip.getSubnetUUID().equals(subnetID)) {
return port.getMacAddress();
+ }
}
}
}
break;
}
}
- if (networkID == null)
+ if (networkID == null) {
return null;
+ }
List<NeutronNetwork> allNetworks = neutronNetworkCache.getAllNetworks();
for (NeutronNetwork network: allNetworks) {
private void processPortUpdate(Node node, OvsdbTerminationPointAugmentation port) {
LOGGER.debug("processPortUpdate <{}> <{}>", node, port);
NeutronNetwork network = tenantNetworkManager.getTenantNetwork(port);
- if (network != null && !network.getRouterExternal()) {
- this.handleInterfaceUpdate(node, port);
+ if (network != null ){
+ if(!network.getRouterExternal()){
+ this.handleInterfaceUpdate(node, port);
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.openstack.netvirt.api;
+
+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 com.google.common.util.concurrent.ListenableFuture;
+
+/**
+*
+* @author Anil Vishnoi (avishnoi@Brocade.com)
+*
+*/
+
+public interface GatewayMacResolver {
+
+ /**
+ * Method will trigger the mac resolution for gateway IP. If user set periodicRefresh to true,
+ * it will periodically trigger the gateway resolution after a specific time interval. If
+ * periodicRefresh is false, it will just do one time gateway resolution.
+ * @param externalNetworkBridgeDpid
+ * @param gatewayIp
+ * @param periodicReferesh
+ * @return
+ */
+ public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
+ final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh);
+
+ /**
+ * Method will stop the periodic refresh of the given gateway ip address.
+ * @param gatewayIp
+ */
+ public void stopPeriodicReferesh(final Ipv4Address gatewayIp);
+}
*/
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
LoadBalancerPoolMember other = (LoadBalancerPoolMember) obj;
if (ipAddr == null) {
- if (other.ipAddr != null)
+ if (other.ipAddr != null) {
return false;
- } else if (!ipAddr.equals(other.ipAddr))
+ }
+ } else if (!ipAddr.equals(other.ipAddr)) {
return false;
+ }
if (macAddr == null) {
- if (other.macAddr != null)
+ if (other.macAddr != null) {
return false;
- } else if (!macAddr.equals(other.macAddr))
+ }
+ } else if (!macAddr.equals(other.macAddr)) {
return false;
+ }
if (port == null) {
- if (other.port != null)
+ if (other.port != null) {
return false;
- } else if (!port.equals(other.port))
+ }
+ } else if (!port.equals(other.port)) {
return false;
+ }
if (protocol == null) {
- if (other.protocol != null)
+ if (other.protocol != null) {
return false;
- } else if (!protocol.equals(other.protocol))
+ }
+ } else if (!protocol.equals(other.protocol)) {
return false;
+ }
return true;
}
public Map<String, LoadBalancerPoolMember> addMember(String uuid, LoadBalancerPoolMember member) {
//If index is not set for this object, update it before inserting
- if (member.getIndex() == -1)
+ if (member.getIndex() == -1) {
member.setIndex(members.size());
+ }
this.members.put(uuid, member);
return this.members;
}
/* Update indices of all other members
*/
int index = 0;
- for(Map.Entry<String, LoadBalancerPoolMember> entry : this.getMembers().entrySet())
+ for(Map.Entry<String, LoadBalancerPoolMember> entry : this.getMembers().entrySet()) {
((LoadBalancerPoolMember) entry.getValue()).setIndex(index++);
+ }
return this.members;
}
public boolean isValid() {
- if (members.size() == 0)
+ if (members.size() == 0) {
return false;
- else if (providerNetworkType == null)
+ } else if (providerNetworkType == null) {
return false;
+ }
return true;
}
if (!eventHandler.awaitTermination(10, TimeUnit.SECONDS)) {
eventHandler.shutdownNow();
// Wait a while for tasks to respond to being cancelled
- if (!eventHandler.awaitTermination(10, TimeUnit.SECONDS))
+ if (!eventHandler.awaitTermination(10, TimeUnit.SECONDS)) {
logger.error("Dispatcher's event handler did not terminate");
+ }
}
} catch (InterruptedException e) {
// (Re-)Cancel if current thread also interrupted
import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
import org.opendaylight.ovsdb.openstack.netvirt.api.*;
import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+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.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
private volatile OutboundNatProvider outboundNatProvider;
private volatile ArpProvider arpProvider;
private volatile RoutingProvider routingProvider;
+ private volatile GatewayMacResolver gatewayMacResolver;
private class FloatIpData {
private final Long dpid; // br-int of node where floating ip is associated with tenant port
private Boolean enabled = false;
private Boolean flgDistributedARPEnabled = true;
private Southbound southbound;
+ private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
final boolean isDelete = action == Action.DELETE;
+ if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
+ if(!isDelete){
+ Node externalBridgeNode = getExternalBridgeNode();
+ if(externalBridgeNode != null){
+ LOGGER.info("Port {} is network router gateway interface, "
+ + "triggering gateway resolution for the attached external network on node {}",neutronPort,externalBridgeNode);
+ this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
+ }else{
+ LOGGER.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
+ }
+ }else{
+ NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
+
+ if(externalNetwork != null){
+ if(externalNetwork.isRouterExternal()){
+ final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
+ if(externalSubnet != null){
+ gatewayMacResolver.stopPeriodicReferesh(new Ipv4Address(externalSubnet.getGatewayIP()));
+ }
+ }
+ }
+ }
+ }
+
// Treat the port event as a router interface event if the port belongs to router. This is a
// helper for handling cases when handleNeutronRouterInterfaceEvent is not available
//
if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
+
for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
NeutronRouter_Interface neutronRouterInterface =
new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
Action actionForNode) {
// Based on the local cache, figure out whether programming needs to occur. To do this, we
// will look at desired action for node.
- //
+
final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr;
final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
return null;
}
+ private Long getDpidForExternalBridge(Node node) {
+ // Check if node is integration bridge; and only then return its dpid
+ if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+ return southbound.getDataPathId(node);
+ }
+ return null;
+ }
+
+ private Node getExternalBridgeNode(){
+ //Pickup the first node that has external bridge (br-ex).
+ //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 thing of better solution.
+ List<Node> allBridges = nodeCacheManager.getBridgeNodes();
+ for(Node node : allBridges){
+ if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
+ NeutronSubnet extSubnet = null;
+ for (NeutronSubnet subnet : neutronSubnetCache.getAllSubnets()){
+ if(subnet.getPortsInSubnet().contains(gatewayPort)){
+ extSubnet = subnet;
+ break;
+ }
+ }
+ return extSubnet;
+ }
+
+ public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
+
+ Preconditions.checkNotNull(node);
+ Preconditions.checkNotNull(gatewayPort);
+ NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
+
+ if(externalNetwork != null){
+ if(externalNetwork.isRouterExternal()){
+ final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
+ if(externalSubnet != null){
+ if(externalSubnet.getGatewayIP() != null){
+ LOGGER.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
+
+ ListenableFuture<MacAddress> gatewayMacAddress =
+ gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
+ new Ipv4Address(externalSubnet.getGatewayIP()),
+ new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
+ new MacAddress(gatewayPort.getMacAddress()),
+ false);
+ if(gatewayMacAddress != null){
+ Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
+ @Override
+ public void onSuccess(MacAddress result) {
+ if(result != null){
+ updateExternalRouterMac(result.getValue());
+ LOGGER.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(),result.getValue());
+ }else{
+ LOGGER.warn("MAC address resolution failed for gateway IP {}",externalSubnet.getGatewayIP());
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOGGER.warn("MAC address resolution failed for gateway IP {}",externalSubnet.getGatewayIP());
+ }
+ }, gatewayMacResolverPool);
+ }
+ }else{
+ LOGGER.warn("No gateway IP address found for external subnet {}",externalSubnet);
+ }
+ }else{
+ LOGGER.warn("Neutron subnet not found for external network {}",externalNetwork);
+ }
+ }
+ }else{
+ LOGGER.warn("Neutron network not found for router interface {}",gatewayPort);
+ }
+ }
+
/**
* Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
*
(NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
southbound =
(Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
-
+ gatewayMacResolver =
+ (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
initL3AdapterMembers();
}
routingProvider = (RoutingProvider)impl;
} else if (impl instanceof L3ForwardingProvider) {
l3ForwardingProvider = (L3ForwardingProvider)impl;
+ }else if (impl instanceof GatewayMacResolver) {
+ gatewayMacResolver = (GatewayMacResolver)impl;
}
}
}
}
public void unsetOvsdbConfigurationService(org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService pluginOvsdbConfigurationService){
- if(this.pluginOvsdbConfigurationService != null)
- this.pluginOvsdbConfigurationService = null;
+ this.pluginOvsdbConfigurationService = null;
}
}
public void unsetOvsdbConnectionService(org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService pluginOvsdbConnectionService){
- if(this.pluginOvsdbConnectionService != null)
- this.pluginOvsdbConnectionService = null;
+ this.pluginOvsdbConnectionService = null;
}
+
@Override
public Connection getConnection(Node node) {
return pluginOvsdbConnectionService.getConnection(NodeUtils.getMdsalNode(node));
}
public void removeOvsdbInventoryListener(OvsdbInventoryListener pluginOvsdbInventoryListener){
- if(this.ovsdbInventoryListeners.contains(ovsdbInventoryListeners))
+ if(this.ovsdbInventoryListeners.contains(ovsdbInventoryListeners)) {
this.ovsdbInventoryListeners.remove(ovsdbInventoryListeners);
+ }
}
@Override
@Override
public void nodeAdded(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
InetAddress address, int port) {
- for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners)
+ for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners) {
listener.nodeAdded(NodeUtils.getSalNode(node), address, port);
+ }
}
@Override
public void nodeRemoved(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node) {
- for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners)
+ for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners) {
listener.nodeRemoved(NodeUtils.getSalNode(node));
+ }
}
@Override
public void rowAdded(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
String tableName, String uuid, Row row) {
- for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners)
+ for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners) {
listener.rowAdded(NodeUtils.getSalNode(node), tableName, uuid, row);
+ }
}
public void rowUpdated(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
String tableName, String uuid, Row old,
Row row) {
- for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners)
+ for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners) {
listener.rowUpdated(NodeUtils.getSalNode(node), tableName, uuid, old, row);
+ }
}
public void rowRemoved(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node node,
String tableName, String uuid, Row row,
Object context) {
- for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners)
+ for(OvsdbInventoryListener listener : this.ovsdbInventoryListeners) {
listener.rowRemoved(NodeUtils.getSalNode(node), tableName, uuid, row, context);
+ }
}
}
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
Connection other = (Connection) obj;
if (identifier == null) {
- if (other.identifier != null) return false;
- } else if (!identifier.equals(other.identifier)) return false;
+ if (other.identifier != null) {
+ return false;
+ }
+ } else if (!identifier.equals(other.identifier)) {
+ return false;
+ }
return true;
}
}
}
public static boolean shouldConfigureController (String databaseName, String tableName) {
- if (autoConfigureController && databaseName.equals(DATABASE_NAME) && tableName.equals("Bridge")) return true;
- return false;
+ return autoConfigureController && databaseName.equals(DATABASE_NAME) && tableName.equals("Bridge");
}
public enum PortType {
@Override
public <T extends TypedBaseTable<?>> String getTableName(Node node, Class<T> typedClass) {
Connection connection = connectionService.getConnection(node);
- if (connection == null) return null;
+ if (connection == null) {
+ return null;
+ }
OvsdbClient client = connection.getClient();
TypedBaseTable<?> typedTable = client.getTypedRowWrapper(typedClass, null);
- if (typedTable == null) return null;
+ if (typedTable == null) {
+ return null;
+ }
return typedTable.getSchema().getName();
}
@Override
public <T extends TypedBaseTable<?>> T getTypedRow(Node node, Class<T> typedClass, Row row) {
Connection connection = connectionService.getConnection(node);
- if (connection == null) return null;
+ if (connection == null) {
+ return null;
+ }
OvsdbClient client = connection.getClient();
return (T)client.getTypedRowWrapper(typedClass, row);
}
@Override
public <T extends TypedBaseTable<?>> T createTypedRow(Node node, Class<T> typedClass) {
Connection connection = connectionService.getConnection(node);
- if (connection == null) return null;
+ if (connection == null) {
+ return null;
+ }
OvsdbClient client = connection.getClient();
return client.createTypedRowWrapper(typedClass);
}
private String getTableNameForRowUuid(Node node, String databaseName, UUID rowUuid) {
ConcurrentMap<String, ConcurrentMap<String, Row>> cache = ovsdbInventoryService.getCache(node, databaseName);
- if (cache == null) return null;
+ if (cache == null) {
+ return null;
+ }
for (String tableName : cache.keySet()) {
ConcurrentMap<String, Row> rows = cache.get(tableName);
if (rows.get(rowUuid.toString()) != null) {
try {
port = Integer.parseInt(params.get(ConnectionConstants.PORT));
- if (port == 0) port = DEFAULT_OVSDB_PORT;
+ if (port == 0) {
+ port = DEFAULT_OVSDB_PORT;
+ }
} catch (Exception e) {
port = DEFAULT_OVSDB_PORT;
}
@Override
public void disconnected(OvsdbClient client) {
Connection connection = ovsdbConnections.get(this.getConnectionIdentifier(client));
- if (connection == null) return;
+ if (connection == null) {
+ return;
+ }
this.disconnect(connection.getNode());
}
}
@Override
public ConcurrentMap<String, ConcurrentMap<String, Row>> getCache(Node n, String databaseName) {
NodeDatabase db = dbCache.get(n);
- if (db == null) return null;
+ if (db == null) {
+ return null;
+ }
return db.getDatabase(databaseName);
}
@Override
public ConcurrentMap<String, Row> getTableCache(Node n, String databaseName, String tableName) {
NodeDatabase db = dbCache.get(n);
- if (db == null) return null;
+ if (db == null) {
+ return null;
+ }
return db.getTableCache(databaseName, tableName);
}
@Override
public Row getRow(Node n, String databaseName, String tableName, String uuid) {
NodeDatabase db = dbCache.get(n);
- if (db == null) return null;
+ if (db == null) {
+ return null;
+ }
return db.getRow(databaseName, tableName, uuid);
}
@Override
public void removeRow(Node n, String databaseName, String tableName, String uuid) {
NodeDatabase db = dbCache.get(n);
- if (db != null) db.removeRow(databaseName, tableName, uuid);
+ if (db != null) {
+ db.removeRow(databaseName, tableName, uuid);
+ }
}
@Override
@Override
public void run() {
try {
- if (ovsdbConfigurationService != null) ovsdbConfigurationService.setOFController(node, uuid.toString());
+ if (ovsdbConfigurationService != null) {
+ ovsdbConfigurationService.setOFController(node, uuid.toString());
+ }
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
prtLn('aliasMap:', 0)
resultMap = {}
for bridge in state.bridgeNodes.values():
- resultMap[ bridge.alias ] = bridge.getOpenflowName()
+ resultMap[ bridge.alias ] = '{0: <25} {1: <7} {2}'.format(bridge.getOpenflowName(), bridge.name, bridge.dpId)
- resultMapKeys = resultMap.keys()
- resultMapKeys.sort()
-
- for resultMapKey in resultMapKeys:
- prtLn('{0}{1: <10} -> {2}'.format(spc, resultMapKey, resultMap[resultMapKey]), 0)
+ for resultMapKey in sorted(resultMap):
+ prtLn('{0}{1: <10} -> {2}'.format(spc, resultMapKey, resultMap[resultMapKey]), 0)
prtLn('', 0)
# --
final String HEXES = "0123456789ABCDEF";
byte[] address = new byte[6];
String[] macBytes = macAddress.split(":");
- if (macBytes.length != 6)
+ if (macBytes.length != 6) {
throw new IllegalArgumentException(
"Specified MAC Address must contain 12 hex digits" +
" separated pairwise by :'s.");
+ }
for (int i = 0; i < 6; ++i) {
address[i] = (byte) ((HEXES.indexOf(macBytes[i].toUpperCase()
.charAt(0)) << 4) | HEXES.indexOf(macBytes[i].toUpperCase()
MacAddress dstMac,
Long etherType) {
EthernetMatchBuilder emb = new EthernetMatchBuilder();
- if (srcMac != null)
+ if (srcMac != null) {
emb.setEthernetSource(new EthernetSourceBuilder()
.setAddress(srcMac)
.build());
- if (dstMac != null)
+ }
+ if (dstMac != null) {
emb.setEthernetDestination(new EthernetDestinationBuilder()
.setAddress(dstMac)
.build());
- if (etherType != null)
+ }
+ if (etherType != null) {
emb.setEthernetType(new EthernetTypeBuilder()
.setType(new EtherType(etherType))
.build());
+ }
return emb.build();
}