import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
import org.opendaylight.controller.sal.binding.api.NotificationService;
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.openflowplugin.learningswitch.multi.LearningSwitchManagerMultiImpl;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
/**
* learning switch activator
+ *
+ * Activator is derived from AbstractBindingAwareConsumer, which takes care
+ * of looking up MD-SAL in Service Registry and registering consumer
+ * when MD-SAL is present.
*/
public class Activator extends AbstractBindingAwareConsumer implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
- private SimpleLearningSwitchManager learningSwitch;
+ private LearningSwitchManager learningSwitch;
@Override
protected void startImpl(BundleContext context) {
LOG.info("startImpl() passing");
- learningSwitch = new SimpleLearningSwitchManager();
}
+ /**
+ * Invoked when consumer is registered to the MD-SAL.
+ *
+ */
@Override
public void onSessionInitialized(ConsumerContext session) {
LOG.info("inSessionInitialized() passing");
- learningSwitch.setData(session.getSALService(DataBrokerService.class));
+ /**
+ * We create instance of our LearningSwitchManager
+ * and set all required dependencies,
+ *
+ * which are
+ * Data Broker (data storage service) - for configuring flows and reading stored switch state
+ * PacketProcessingService - for sending out packets
+ * NotificationService - for receiving notifications such as packet in.
+ *
+ */
+ learningSwitch = new LearningSwitchManagerMultiImpl();
+ learningSwitch.setDataBroker(session.getSALService(DataBrokerService.class));
learningSwitch.setPacketProcessingService(session.getRpcService(PacketProcessingService.class));
learningSwitch.setNotificationService(session.getSALService(NotificationService.class));
learningSwitch.start();
}
@Override
- public void close() throws Exception {
+ public void close() {
LOG.info("close() passing");
if (learningSwitch != null) {
learningSwitch.stop();
}
}
-
+
+ @Override
+ protected void stopImpl(BundleContext context) {
+ close();
+ super.stopImpl(context);
+ }
}
/**
*
*/
-public interface DataChangeListenerRegistrationPublisher {
+public interface DataChangeListenerRegistrationHolder {
/**
* @return the dataChangeListenerRegistration
*/
- public abstract ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration();
+ ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration();
}
/**
*
*/
-public interface OFDataStoreAccessor {
+public interface FlowCommitWrapper {
/**
+ * Starts and commits data change transaction which
+ * modifies provided flow path with supplied body.
+ *
* @param flowPath
- * @param allToCtrlFlow
+ * @param flowBody
* @return transaction commit
*
*/
- Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath, Flow allToCtrlFlow);
+ Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath, Flow flowBody);
}
/**
*
*/
-public class OFDataStoreAccessorImpl implements OFDataStoreAccessor {
+public class FlowCommitWrapperImpl implements FlowCommitWrapper {
private DataBrokerService dataBrokerService;
/**
* @param dataBrokerService
*/
- public OFDataStoreAccessorImpl(DataBrokerService dataBrokerService) {
+ public FlowCommitWrapperImpl(DataBrokerService dataBrokerService) {
this.dataBrokerService = dataBrokerService;
}
@Override
public Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath,
- Flow allToCtrlFlow) {
+ Flow flowBody) {
DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
- addFlowTransaction.putConfigurationData(flowPath, allToCtrlFlow);
+ addFlowTransaction.putConfigurationData(flowPath, flowBody);
return addFlowTransaction.commit();
}
-
}
--- /dev/null
+package org.opendaylight.openflowplugin.learningswitch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.inventory.rev130819.FlowId;
+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.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+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.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.flow.types.rev131026.instruction.list.InstructionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+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.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+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 com.google.common.collect.ImmutableList;
+
+public class FlowUtils {
+ /**
+ * @param tableId
+ * @param priority
+ * @param srcMac
+ * @param dstMac
+ * @param dstPort
+ * @return {@link FlowBuilder} forwarding all packets to controller port
+ */
+ public static FlowBuilder createDirectMacToMacFlow(Short tableId, int priority, MacAddress srcMac,
+ MacAddress dstMac, NodeConnectorRef dstPort) {
+ FlowBuilder macToMacFlow = new FlowBuilder() //
+ .setTableId(tableId) //
+ .setFlowName("mac2mac");
+ macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+ EthernetMatch ethernetMatch = new EthernetMatchBuilder() //
+ .setEthernetSource(new EthernetSourceBuilder() //
+ .setAddress(srcMac) //
+ .build()) //
+ .setEthernetDestination(new EthernetDestinationBuilder() //
+ .setAddress(dstMac) //
+ .build()) //
+ .build();
+
+ MatchBuilder match = new MatchBuilder();
+ match.setEthernetMatch(ethernetMatch);
+
+ Uri outputPort = dstPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+ Action outputToControllerAction = new ActionBuilder() //
+ .setAction(new OutputActionCaseBuilder() //
+ .setOutputAction(new OutputActionBuilder() //
+ .setMaxLength(new Integer(0xffff)) //
+ .setOutputNodeConnector(outputPort) //
+ .build()) //
+ .build()) //
+ .build();
+
+ // Create an Apply Action
+ ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+ .build();
+
+ // Wrap our Apply Action in an Instruction
+ Instruction applyActionsInstruction = new InstructionBuilder() //
+ .setInstruction(new ApplyActionsCaseBuilder()//
+ .setApplyActions(applyActions) //
+ .build()) //
+ .build();
+
+ // Put our Instruction in a list of Instructions
+
+ macToMacFlow //
+ .setMatch(new MatchBuilder() //
+ .setEthernetMatch(ethernetMatch) //
+ .build()) //
+ .setInstructions(new InstructionsBuilder() //
+ .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+ .build()) //
+ .setPriority(priority) //
+ .setBufferId(0L) //
+ .setHardTimeout(0) //
+ .setIdleTimeout(0) //
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ return macToMacFlow;
+ }
+
+ /**
+ * @param tableId
+ * @param priority
+ * @param flowId
+ * @return {@link FlowBuilder} forwarding all packets to controller port
+ */
+ public static FlowBuilder createFwdAllToControllerFlow(Short tableId, int priority, FlowId flowId) {
+ FlowBuilder allToCtrlFlow = new FlowBuilder().setTableId(tableId).setFlowName("allPacketsToCtrl").setId(flowId)
+ .setKey(new FlowKey(flowId));
+
+ MatchBuilder matchBuilder = new MatchBuilder();
+
+ // Create output action -> send to controller
+ OutputActionBuilder output = new OutputActionBuilder();
+ output.setMaxLength(new Integer(0xffff));
+ Uri controllerPort = new Uri(OutputPortValues.CONTROLLER.toString());
+ output.setOutputNodeConnector(controllerPort);
+
+ ActionBuilder ab = new ActionBuilder();
+ ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
+ ab.setOrder(0);
+ ab.setKey(new ActionKey(0));
+
+ List<Action> actionList = new ArrayList<Action>();
+ actionList.add(ab.build());
+
+ // Create an Apply Action
+ ApplyActionsBuilder aab = new ApplyActionsBuilder();
+ aab.setAction(actionList);
+
+ // Wrap our Apply Action in an Instruction
+ InstructionBuilder ib = new InstructionBuilder();
+ ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+ ib.setOrder(0);
+ ib.setKey(new InstructionKey(0));
+
+ // Put our Instruction in a list of Instructions
+ InstructionsBuilder isb = new InstructionsBuilder();
+ List<Instruction> instructions = new ArrayList<Instruction>();
+ instructions.add(ib.build());
+ isb.setInstruction(instructions);
+
+ allToCtrlFlow //
+ .setMatch(matchBuilder.build()) //
+ .setInstructions(isb.build()) //
+ .setPriority(priority) //
+ .setBufferId(0L) //
+ .setHardTimeout(0) //
+ .setIdleTimeout(0) //
+ .setFlags(new FlowModFlags(false, false, false, false, false));
+
+ return allToCtrlFlow;
+ }
+}
--- /dev/null
+package org.opendaylight.openflowplugin.learningswitch;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+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.FlowKey;
+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.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+
+public class InstanceIdentifierUtils {
+
+ /**
+ * Creates an Instance Identifier (path) for node with specified id
+ *
+ * @param nodeId
+ * @return
+ */
+ public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+ return InstanceIdentifier.builder(Nodes.class) //
+ .child(Node.class, new NodeKey(nodeId)) //
+ .build();
+ }
+
+ /**
+ * Shorten's node child path to node path.
+ *
+ * @param nodeChild child of node, from which we want node path.
+ * @return
+ */
+ public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+ return nodeChild.firstIdentifierOf(Node.class);
+ }
+
+
+ /**
+ * Creates a table path by appending table specific location to node path
+ *
+ * @param nodePath
+ * @param tableKey
+ * @return
+ */
+ public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath,TableKey tableKey) {
+ return InstanceIdentifier.builder(nodePath)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, tableKey)
+ .build();
+ }
+
+ /**
+ * Creates a path for particular flow, by appending flow-specific information
+ * to table path.
+ *
+ * @param flowId
+ * @param tablePathArg
+ * @return path to flow
+ */
+ public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table,FlowKey flowKey) {
+ return InstanceIdentifier.builder(table)
+ .child(Flow.class, flowKey)
+ .build();
+ }
+
+ /**
+ * Extract table id from table path.
+ *
+ * @param tablePath
+ * @return
+ */
+ public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+ return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+ }
+
+ /**
+ * Extracts NodeConnectorKey from node connector path.
+ *
+ */
+ public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+ return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ }
+
+
+ //
+ public static final InstanceIdentifier<NodeConnector> createNodeConnectorPath(InstanceIdentifier<Node> nodeKey,NodeConnectorKey nodeConnectorKey) {
+ return InstanceIdentifier.builder(nodeKey) //
+ .child(NodeConnector.class,nodeConnectorKey) //
+ .build();
+ }
+}
* 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.openflowplugin.learningswitch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-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.yangtools.yang.binding.InstanceIdentifier;
/**
*
*/
-public interface SimpleLearningSwitchHandler extends PacketProcessingListener {
+public interface LearningSwitchHandler {
/**
* @param tablePath
/**
* @param dataStoreAccessor the dataStoreAccessor to set
*/
- void setDataStoreAccessor(OFDataStoreAccessor dataStoreAccessor);
+ void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor);
/**
* @param registrationPublisher the registrationPublisher to set
*/
- void setRegistrationPublisher(DataChangeListenerRegistrationPublisher registrationPublisher);
+ void setRegistrationPublisher(DataChangeListenerRegistrationHolder registrationPublisher);
}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.learningswitch;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+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.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.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.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.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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple Learning Switch implementation which does mac learning for one switch.
+ *
+ *
+ */
+public class LearningSwitchHandlerSimpleImpl implements LearningSwitchHandler, PacketProcessingListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LearningSwitchHandler.class);
+
+ private static final byte[] ETH_TYPE_IPV4 = new byte[] { 0x08, 0x00 };
+
+ private static final int DIRECT_FLOW_PRIORITY = 512;
+
+ private DataChangeListenerRegistrationHolder registrationPublisher;
+ private FlowCommitWrapper dataStoreAccessor;
+ private PacketProcessingService packetProcessingService;
+
+ private boolean iAmLearning = false;
+
+ private NodeId nodeId;
+ private AtomicLong flowIdInc = new AtomicLong();
+ private InstanceIdentifier<Node> nodePath;
+ private InstanceIdentifier<Table> tablePath;
+
+ private Map<MacAddress, NodeConnectorRef> mac2portMapping;
+ private Set<String> coveredMacPaths;
+
+ @Override
+ public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
+ if (iAmLearning) {
+ LOG.debug("already learning a node, skipping {}", nodeId.getValue());
+ return;
+ }
+
+ LOG.debug("expected table acquired, learning ..");
+
+ // disable listening - simple learning handles only one node (switch)
+ if (registrationPublisher != null) {
+ try {
+ LOG.debug("closing dataChangeListenerRegistration");
+ registrationPublisher.getDataChangeListenerRegistration().close();
+ } catch (Exception e) {
+ LOG.error("closing registration upon flowCapable node update listener failed: " + e.getMessage(), e);
+ }
+ }
+
+ iAmLearning = true;
+
+ tablePath = appearedTablePath;
+ nodePath = tablePath.firstIdentifierOf(Node.class);
+ nodeId = nodePath.firstKeyOf(Node.class, NodeKey.class).getId();
+ mac2portMapping = new HashMap<>();
+ coveredMacPaths = new HashSet<>();
+
+ // start forwarding all packages to controller
+ FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+ FlowKey flowKey = new FlowKey(flowId);
+ InstanceIdentifier<Flow> flowPath = InstanceIdentifierUtils.createFlowPath(tablePath, flowKey);
+
+ int priority = 0;
+ // create flow in table with id = 0, priority = 4 (other params are
+ // defaulted in OFDataStoreUtil)
+ FlowBuilder allToCtrlFlow = FlowUtils.createFwdAllToControllerFlow(
+ InstanceIdentifierUtils.getTableId(tablePath), priority, flowId);
+
+ LOG.debug("writing packetForwardToController flow");
+ dataStoreAccessor.writeFlowToConfig(flowPath, allToCtrlFlow.build());
+ }
+
+ @Override
+ public void setRegistrationPublisher(DataChangeListenerRegistrationHolder registrationPublisher) {
+ this.registrationPublisher = registrationPublisher;
+ }
+
+ @Override
+ public void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor) {
+ this.dataStoreAccessor = dataStoreAccessor;
+ }
+
+ @Override
+ public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ @Override
+ public void onPacketReceived(PacketReceived notification) {
+ if (!iAmLearning) {
+ // ignoring packets - this should not happen
+ return;
+ }
+
+ LOG.debug("Received packet via match: {}", notification.getMatch());
+
+ // detect and compare node - we support one switch
+ if (!nodePath.contains(notification.getIngress().getValue())) {
+ return;
+ }
+
+ // read src MAC and dst MAC
+ byte[] dstMacRaw = PacketUtils.extractDstMac(notification.getPayload());
+ byte[] srcMacRaw = PacketUtils.extractSrcMac(notification.getPayload());
+ byte[] etherType = PacketUtils.extractEtherType(notification.getPayload());
+
+ MacAddress dstMac = PacketUtils.rawMacToMac(dstMacRaw);
+ MacAddress srcMac = PacketUtils.rawMacToMac(srcMacRaw);
+
+ NodeConnectorKey ingressKey = InstanceIdentifierUtils.getNodeConnectorKey(notification.getIngress().getValue());
+
+ LOG.debug("Received packet from MAC match: {}, ingress: {}", srcMac, ingressKey.getId());
+ LOG.debug("Received packet to MAC match: {}", dstMac);
+ LOG.debug("Ethertype: {}", Integer.toHexString(0x0000ffff & ByteBuffer.wrap(etherType).getShort()));
+
+ // learn by IPv4 traffic only
+ if (Arrays.equals(ETH_TYPE_IPV4, etherType)) {
+ NodeConnectorRef previousPort = mac2portMapping.put(srcMac, notification.getIngress());
+ if (previousPort != null && !notification.getIngress().equals(previousPort)) {
+ NodeConnectorKey previousPortKey = InstanceIdentifierUtils.getNodeConnectorKey(previousPort.getValue());
+ LOG.debug("mac2port mapping changed by mac {}: {} -> {}", srcMac, previousPortKey, ingressKey.getId());
+ }
+ // if dst MAC mapped:
+ NodeConnectorRef destNodeConnector = mac2portMapping.get(dstMac);
+ if (destNodeConnector != null) {
+ synchronized (coveredMacPaths) {
+ if (!destNodeConnector.equals(notification.getIngress())) {
+ // add flow
+ addBridgeFlow(srcMac, dstMac, destNodeConnector);
+ addBridgeFlow(dstMac, srcMac, notification.getIngress());
+ } else {
+ LOG.debug("useless rule ignoring - both MACs are behind the same port");
+ }
+ }
+ LOG.debug("packetIn-directing.. to {}",
+ InstanceIdentifierUtils.getNodeConnectorKey(destNodeConnector.getValue()).getId());
+ sendPacketOut(notification.getPayload(), notification.getIngress(), destNodeConnector);
+ } else {
+ // flood
+ LOG.debug("packetIn-still flooding.. ");
+ flood(notification.getPayload(), notification.getIngress());
+ }
+ } else {
+ // non IPv4 package
+ flood(notification.getPayload(), notification.getIngress());
+ }
+
+ }
+
+ /**
+ * @param srcMac
+ * @param dstMac
+ * @param destNodeConnector
+ */
+ private void addBridgeFlow(MacAddress srcMac, MacAddress dstMac, NodeConnectorRef destNodeConnector) {
+ synchronized (coveredMacPaths) {
+ String macPath = srcMac.toString() + dstMac.toString();
+ if (!coveredMacPaths.contains(macPath)) {
+ LOG.debug("covering mac path: {} by [{}]", macPath,
+ destNodeConnector.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId());
+
+ coveredMacPaths.add(macPath);
+ FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+ FlowKey flowKey = new FlowKey(flowId);
+ /**
+ * Path to the flow we want to program.
+ */
+ InstanceIdentifier<Flow> flowPath = InstanceIdentifierUtils.createFlowPath(tablePath, flowKey);
+
+ Short tableId = InstanceIdentifierUtils.getTableId(tablePath);
+ FlowBuilder srcToDstFlow = FlowUtils.createDirectMacToMacFlow(tableId, DIRECT_FLOW_PRIORITY, srcMac,
+ dstMac, destNodeConnector);
+
+ dataStoreAccessor.writeFlowToConfig(flowPath, srcToDstFlow.build());
+ }
+ }
+ }
+
+ private void flood(byte[] payload, NodeConnectorRef ingress) {
+ NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(nodeConnectorId("0xfffffffb"));
+ InstanceIdentifier<?> nodeConnectorPath = InstanceIdentifierUtils.createNodeConnectorPath(nodePath, nodeConnectorKey);
+ NodeConnectorRef egressConnectorRef = new NodeConnectorRef(nodeConnectorPath);
+
+ sendPacketOut(payload, ingress, egressConnectorRef);
+ }
+
+ private NodeConnectorId nodeConnectorId(String connectorId) {
+ NodeKey nodeKey = nodePath.firstKeyOf(Node.class, NodeKey.class);
+ StringBuilder stringId = new StringBuilder(nodeKey.getId().getValue()).append(":").append(connectorId);
+ return new NodeConnectorId(stringId.toString());
+ }
+
+ private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+ InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+ TransmitPacketInput input = new TransmitPacketInputBuilder() //
+ .setPayload(payload) //
+ .setNode(new NodeRef(egressNodePath)) //
+ .setEgress(egress) //
+ .setIngress(ingress) //
+ .build();
+ packetProcessingService.transmitPacket(input);
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.learningswitch;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+
+/**
+ *
+ */
+public interface LearningSwitchManager {
+
+ /**
+ * stop manager
+ */
+ void stop();
+
+ /**
+ * start manager
+ */
+ void start();
+
+ /**
+ * Set's Data Broker dependency.
+ *
+ * Data Broker is used to access overal operational and configuration
+ * tree.
+ *
+ * In simple Learning Switch handler, data broker is used to listen
+ * for changes in Openflow tables and to configure flows which will
+ * be provisioned down to the Openflow switch.
+ *
+ * inject {@link DataBrokerService}
+ * @param data
+ */
+ void setDataBroker(DataBrokerService data);
+
+ /**
+ * Set's Packet Processing dependency.
+ *
+ * Packet Processing service is used to send packet Out on Openflow
+ * switch.
+ *
+ * inject {@link PacketProcessingService}
+ *
+ * @param packetProcessingService
+ */
+ void setPacketProcessingService(
+ PacketProcessingService packetProcessingService);
+
+ /**
+ * Set's Notification service dependency.
+ *
+ * Notification service is used to register for listening
+ * packet-in notifications.
+ *
+ * inject {@link NotificationService}
+ * @param notificationService
+ */
+ void setNotificationService(NotificationService notificationService);
+}
* corresponding MACs)</li>
* </ul>
*/
-public class SimpleLearningSwitchManager implements DataChangeListenerRegistrationPublisher {
+public class LearningSwitchManagerSimpleImpl implements DataChangeListenerRegistrationHolder,
+ LearningSwitchManager {
protected static final Logger LOG = LoggerFactory
- .getLogger(SimpleLearningSwitchManager.class);
+ .getLogger(LearningSwitchManagerSimpleImpl.class);
private NotificationService notificationService;
private PacketProcessingService packetProcessingService;
/**
* @param notificationService the notificationService to set
*/
+ @Override
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
/**
* @param packetProcessingService the packetProcessingService to set
*/
+ @Override
public void setPacketProcessingService(
PacketProcessingService packetProcessingService) {
this.packetProcessingService = packetProcessingService;
/**
* @param data the data to set
*/
- public void setData(DataBrokerService data) {
+ @Override
+ public void setDataBroker(DataBrokerService data) {
this.data = data;
}
/**
* starting learning switch
*/
+ @Override
public void start() {
LOG.debug("start() -->");
- OFDataStoreAccessor dataStoreAccessor = new OFDataStoreAccessorImpl(data);
+ FlowCommitWrapper dataStoreAccessor = new FlowCommitWrapperImpl(data);
- SimpleLearningSwitchHandler learningSwitchHandler = new SimpleLearningSwitchHandlerImpl();
+ LearningSwitchHandlerSimpleImpl learningSwitchHandler = new LearningSwitchHandlerSimpleImpl();
learningSwitchHandler.setRegistrationPublisher(this);
learningSwitchHandler.setDataStoreAccessor(dataStoreAccessor);
learningSwitchHandler.setPacketProcessingService(packetProcessingService);
- //FIXME: should register for packets from particular node
packetInRegistration = notificationService.registerNotificationListener(learningSwitchHandler);
WakeupOnNode wakeupListener = new WakeupOnNode();
/**
* stopping learning switch
*/
+ @Override
public void stop() {
LOG.debug("stop() -->");
//TODO: remove flow (created in #start())
+++ /dev/null
-/**
- * Copyright (c) 2014 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.openflowplugin.learningswitch;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-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.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.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.types.rev131026.FlowModFlags;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
-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.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.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.flow.types.rev131026.instruction.list.InstructionKey;
-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.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.ethernet.match.fields.EthernetDestinationBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
-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;
-
-/**
- *
- */
-public abstract class OFFlowUtil {
-
- /**
- * @param tableId
- * @param priority
- * @param srcMac
- * @param dstMac
- * @param dstPort
- * @return {@link FlowBuilder} forwarding all packets to controller port
- */
- public static FlowBuilder createDirectMacToMacFlow(Short tableId, int priority,
- MacAddress srcMac, MacAddress dstMac, NodeConnectorRef dstPort) {
- FlowBuilder allToCtrlFlow = new FlowBuilder();
- allToCtrlFlow.setTableId(tableId);
- allToCtrlFlow.setFlowName("mac2mac");
- allToCtrlFlow.setId(new FlowId(Long.toString(allToCtrlFlow.hashCode())));
-
- MatchBuilder match = new MatchBuilder();
- EthernetMatchBuilder ethernetMatch = new EthernetMatchBuilder();
- EthernetSourceBuilder ethSourceBuilder = new EthernetSourceBuilder();
- ethSourceBuilder.setAddress(srcMac);
- ethernetMatch.setEthernetSource(ethSourceBuilder.build());
- EthernetDestinationBuilder ethDestinationBuilder = new EthernetDestinationBuilder();
- ethDestinationBuilder.setAddress(dstMac);
- ethernetMatch.setEthernetDestination(ethDestinationBuilder.build());
- match.setEthernetMatch(ethernetMatch.build());
-
- OutputActionBuilder output = new OutputActionBuilder();
- output.setMaxLength(new Integer(0xffff));
- Uri outputPort = dstPort.getValue().firstKeyOf(
- NodeConnector.class, NodeConnectorKey.class).getId();
- output.setOutputNodeConnector(outputPort);
- ActionBuilder ab = new ActionBuilder();
- ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
-
- // Add our drop action to a list
- ArrayList<Action> actionList = new ArrayList<>();
- actionList.add(ab.build());
-
- // Create an Apply Action
- ApplyActionsBuilder aab = new ApplyActionsBuilder();
- aab.setAction(actionList);
-
- // Wrap our Apply Action in an Instruction
- InstructionBuilder ib = new InstructionBuilder();
- ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
-
- // Put our Instruction in a list of Instructions
- InstructionsBuilder isb = new InstructionsBuilder();
- ArrayList<Instruction> instructions = new ArrayList<Instruction>();
- instructions.add(ib.build());
- isb.setInstruction(instructions);
-
- allToCtrlFlow.setMatch(match.build())
- .setInstructions(isb.build())
- .setPriority(priority)
- .setBufferId(0L)
- .setHardTimeout(0)
- .setIdleTimeout(0)
- .setFlags(new FlowModFlags(false, false, false, false, false));
-
- return allToCtrlFlow;
- }
-
- /**
- * @param tableId
- * @param priority
- * @param flowId
- * @return {@link FlowBuilder} forwarding all packets to controller port
- */
- public static FlowBuilder createFwdAllToControllerFlow(Short tableId, int priority, FlowId flowId) {
- FlowBuilder allToCtrlFlow = new FlowBuilder();
- allToCtrlFlow.setTableId(tableId);
- allToCtrlFlow.setFlowName("allPacketsToCtrl");
- allToCtrlFlow.setId(flowId);
- allToCtrlFlow.setKey(new FlowKey(flowId));
-
- MatchBuilder emptyMatchBuilder = new MatchBuilder();
-
- // Create output action -> send to controller
- OutputActionBuilder output = new OutputActionBuilder();
- output.setMaxLength(new Integer(0xffff));
- Uri controllerPort = new Uri(OutputPortValues.CONTROLLER.toString());
- output.setOutputNodeConnector(controllerPort);
-
- ActionBuilder ab = new ActionBuilder();
- ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
- ab.setOrder(0);
- ab.setKey(new ActionKey(0));
-
- List<Action> actionList = new ArrayList<Action>();
- actionList.add(ab.build());
-
- // Create an Apply Action
- ApplyActionsBuilder aab = new ApplyActionsBuilder();
- aab.setAction(actionList);
-
- // Wrap our Apply Action in an Instruction
- InstructionBuilder ib = new InstructionBuilder();
- ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
- ib.setOrder(0);
- ib.setKey(new InstructionKey(0));
-
- // Put our Instruction in a list of Instructions
- InstructionsBuilder isb = new InstructionsBuilder();
- List<Instruction> instructions = new ArrayList<Instruction>();
- instructions.add(ib.build());
- isb.setInstruction(instructions);
-
- allToCtrlFlow.setMatch(emptyMatchBuilder.build());
- allToCtrlFlow.setInstructions(isb.build());
- allToCtrlFlow.setPriority(priority);
- allToCtrlFlow.setBufferId(0L);
- allToCtrlFlow.setHardTimeout(300);
- allToCtrlFlow.setIdleTimeout(240);
- allToCtrlFlow.setFlags(new FlowModFlags(false, false, false, false, false));
-
- return allToCtrlFlow;
- }
-
- /**
- * @param flowId
- * @param tablePathArg
- * @return
- */
- static InstanceIdentifier<Flow> assemleFlowPath(FlowId flowId, InstanceIdentifier<Table> tablePathArg) {
- FlowKey flowKey = new FlowKey(flowId);
- InstanceIdentifier<Flow> flowPath = InstanceIdentifier.builder(tablePathArg)
- .child(Flow.class, flowKey )
- .toInstance();
- return flowPath;
- }
-
- /**
- * @param payload
- * @return
- */
- static byte[] extractDstMac(byte[] payload) {
- return Arrays.copyOfRange(payload, 0, 6);
- }
-
- /**
- * @param payload
- * @return
- */
- static byte[] extractSrcMac(byte[] payload) {
- return Arrays.copyOfRange(payload, 6, 12);
- }
-
- static MacAddress rawMacToMac(byte[] rawMac) {
- MacAddress mac = null;
- if (rawMac != null && rawMac.length == 6) {
- StringBuffer sb = new StringBuffer();
- for (byte octet : rawMac) {
- sb.append(String.format(":%02X", octet));
- }
- mac = new MacAddress(sb.substring(1));
- }
- return mac;
- }
-
- static TransmitPacketInput buildPacketOut(byte[] payload, NodeConnectorRef ingress,
- String outputPort, NodeKey nodekey) {
- InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder(Nodes.class)
- .child(Node.class, nodekey).toInstance();
-
- NodeConnectorRef egressConfRef = new NodeConnectorRef(
- createNodeConnRef(nodePath, nodekey, outputPort));
-
- TransmitPacketInputBuilder tPackBuilder = new TransmitPacketInputBuilder();
- tPackBuilder.setPayload(payload);
- tPackBuilder.setNode(new NodeRef(nodePath));
- tPackBuilder.setCookie(null);
- tPackBuilder.setEgress(egressConfRef);
- tPackBuilder.setIngress(ingress);
- return tPackBuilder.build();
- }
-
- private static NodeConnectorRef createNodeConnRef(InstanceIdentifier<Node> nodeInstId,
- NodeKey nodeKey, String port) {
- StringBuilder sBuild = new StringBuilder(nodeKey.getId().getValue()).append(":").append(port);
- NodeConnectorKey nConKey = new NodeConnectorKey(new NodeConnectorId(sBuild.toString()));
- InstanceIdentifier<NodeConnector> portPath = InstanceIdentifier.builder(nodeInstId)
- .child(NodeConnector.class, nConKey).toInstance();
- return new NodeConnectorRef(portPath);
- }
-
-}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.learningswitch;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+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.inventory.rev130819.FlowId;
+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.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+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.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.flow.types.rev131026.instruction.list.InstructionKey;
+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.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.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+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.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 com.google.common.collect.ImmutableList;
+
+/**
+ *
+ */
+public abstract class PacketUtils {
+
+
+
+ /**
+ * @param payload
+ * @return destination MAC address
+ */
+ public static byte[] extractDstMac(byte[] payload) {
+ return Arrays.copyOfRange(payload, 0, 6);
+ }
+
+ /**
+ * @param payload
+ * @return source MAC address
+ */
+ public static byte[] extractSrcMac(byte[] payload) {
+ return Arrays.copyOfRange(payload, 6, 12);
+ }
+
+ /**
+ * @param payload
+ * @return source MAC address
+ */
+ public static byte[] extractEtherType(byte[] payload) {
+ return Arrays.copyOfRange(payload, 12, 14);
+ }
+
+ /**
+ * @param rawMac
+ * @return {@link MacAddress} wrapping string value, baked upon binary MAC
+ * address
+ */
+ public static MacAddress rawMacToMac(byte[] rawMac) {
+ MacAddress mac = null;
+ if (rawMac != null && rawMac.length == 6) {
+ StringBuffer sb = new StringBuffer();
+ for (byte octet : rawMac) {
+ sb.append(String.format(":%02X", octet));
+ }
+ mac = new MacAddress(sb.substring(1));
+ }
+ return mac;
+ }
+
+ /**
+ * @param nodeInstId
+ * @param nodeKey
+ * @param port
+ * @return port wrapped into {@link NodeConnectorRef}
+ */
+ public static NodeConnectorRef createNodeConnRef(InstanceIdentifier<Node> nodeInstId, NodeKey nodeKey, String port) {
+ StringBuilder sBuild = new StringBuilder(nodeKey.getId().getValue()).append(":").append(port);
+ NodeConnectorKey nConKey = new NodeConnectorKey(new NodeConnectorId(sBuild.toString()));
+ InstanceIdentifier<NodeConnector> portPath = InstanceIdentifier.builder(nodeInstId)
+ .child(NodeConnector.class, nConKey).toInstance();
+ return new NodeConnectorRef(portPath);
+ }
+}
+++ /dev/null
-/**
- * Copyright (c) 2014 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.openflowplugin.learningswitch;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
-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.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.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-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.PacketReceived;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- */
-public class SimpleLearningSwitchHandlerImpl implements SimpleLearningSwitchHandler {
-
- private static final Logger LOG = LoggerFactory
- .getLogger(SimpleLearningSwitchHandler.class);
-
- private DataChangeListenerRegistrationPublisher registrationPublisher;
- private OFDataStoreAccessor dataStoreAccessor;
- private PacketProcessingService packetProcessingService;
-
- private boolean iAmLearning = false;
-
- private NodeId nodeId;
- private AtomicLong flowIdInc = new AtomicLong();
- private InstanceIdentifier<Node> nodePath;
- private InstanceIdentifier<Table> tablePath;
-
- private Map<MacAddress, NodeConnectorRef> mac2portMapping;
- private Set<String> coveredMacPaths;
-
- @Override
- public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
- if (iAmLearning) {
- LOG.debug("already learning a node, skipping {}", nodeId.getValue());
- return;
- }
-
- LOG.debug("expected table acquired, learning ..");
-
- // disable listening - simple learning handles only one node (switch)
- try {
- LOG.debug("closing dataChangeListenerRegistration");
- registrationPublisher.getDataChangeListenerRegistration().close();
- } catch (Exception e) {
- LOG.error("closing registration upon flowCapable node update listener failed: " + e.getMessage(), e);
- }
- iAmLearning = true;
- tablePath = appearedTablePath;
- nodePath = tablePath.firstIdentifierOf(Node.class);
- nodeId = nodePath.firstKeyOf(Node.class, NodeKey.class).getId();
- mac2portMapping = new HashMap<>();
- coveredMacPaths = new HashSet<>();
-
- // start forwarding all packages to controller
- FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
- InstanceIdentifier<Flow> flowPath = OFFlowUtil.assemleFlowPath(flowId, tablePath);
-
- // create flow in table with id = 0, priority = 4 (other params are defaulted in OFDataStoreUtil)
- FlowBuilder allToCtrlFlow = OFFlowUtil.createFwdAllToControllerFlow(
- appearedTablePath.firstKeyOf(Table.class, TableKey.class).getId(), 0, flowId);
-
- LOG.debug("writing packetForwardToController flow");
- dataStoreAccessor.writeFlowToConfig(flowPath, allToCtrlFlow.build());
- }
-
- @Override
- public void setRegistrationPublisher(
- DataChangeListenerRegistrationPublisher registrationPublisher) {
- this.registrationPublisher = registrationPublisher;
- }
-
- @Override
- public void setDataStoreAccessor(OFDataStoreAccessor dataStoreAccessor) {
- this.dataStoreAccessor = dataStoreAccessor;
- }
-
- @Override
- public void setPacketProcessingService(
- PacketProcessingService packetProcessingService) {
- this.packetProcessingService = packetProcessingService;
- }
-
- @Override
- public void onPacketReceived(PacketReceived notification) {
- if (!iAmLearning) {
- // ignoring packets - this should not happen
- return;
- }
-
- LOG.debug("Received packet via match: {}", notification.getMatch());
-
- // detect and compare node - we support one switch
- if (!nodePath.contains(notification.getIngress().getValue())) {
- return;
- }
-
- // read src MAC and dst MAC
- byte[] dstMacRaw = OFFlowUtil.extractDstMac(notification.getPayload());
- byte[] srcMacRaw = OFFlowUtil.extractSrcMac(notification.getPayload());
-
- MacAddress dstMac = OFFlowUtil.rawMacToMac(dstMacRaw);
- MacAddress srcMac = OFFlowUtil.rawMacToMac(srcMacRaw);
-
- LOG.debug("Received packet from MAC match: {}, ingress: {}", srcMac, notification.getIngress());
- LOG.debug("Received packet to MAC match: {}", dstMac);
-
- mac2portMapping.put(srcMac, notification.getIngress());
-
- // if dst MAC mapped:
- NodeConnectorRef dstNodeConnectorRef = mac2portMapping.get(dstMac);
- if (dstNodeConnectorRef != null) {
- synchronized (coveredMacPaths) {
- // add flow
- addBridgeFlow(srcMac, dstMac, dstNodeConnectorRef);
- addBridgeFlow(dstMac, srcMac, notification.getIngress());
- }
- }
- // flood
- flood(notification.getPayload(), notification.getIngress());
- }
-
- /**
- * @param srcMac
- * @param dstMac
- * @param dstNodeConnectorRef
- */
- private void addBridgeFlow(MacAddress srcMac, MacAddress dstMac,
- NodeConnectorRef dstNodeConnectorRef) {
- synchronized (coveredMacPaths) {
- String macPath = srcMac.toString() + dstMac.toString();
- if (!coveredMacPaths.contains(macPath)) {
- LOG.debug("covering mac path: {}", macPath);
- coveredMacPaths.add(macPath);
- FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
-
- // create flow in table with id = 0, priority = 2 (other params are defaulted in OFDataStoreUtil)
- InstanceIdentifier<Flow> flowPath = OFFlowUtil.assemleFlowPath(flowId, tablePath);
- FlowBuilder srcToDstFlow = OFFlowUtil.createDirectMacToMacFlow((short) 0, 512,
- srcMac, dstMac, dstNodeConnectorRef);
-
- dataStoreAccessor.writeFlowToConfig(flowPath, srcToDstFlow.build());
- }
- }
- }
-
- private void flood(byte[] payload, NodeConnectorRef ingress) {
- NodeKey nodeKey = new NodeKey(nodeId);
-
- TransmitPacketInput input = OFFlowUtil.buildPacketOut(payload, ingress, "0xfffffffb", nodeKey);
- packetProcessingService.transmitPacket(input);
- }
-}
private static final Logger LOG = LoggerFactory
.getLogger(WakeupOnNode.class);
- private SimpleLearningSwitchHandler learningSwitchHandler;
+ private LearningSwitchHandler learningSwitchHandler;
@Override
public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
* @param learningSwitchHandler the learningSwitchHandler to set
*/
public void setLearningSwitchHandler(
- SimpleLearningSwitchHandler learningSwitchHandler) {
+ LearningSwitchHandler learningSwitchHandler) {
this.learningSwitchHandler = learningSwitchHandler;
}
}
--- /dev/null
+/**
+ * Copyright (c) 2013 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.openflowplugin.learningswitch.multi;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.openflowplugin.learningswitch.DataChangeListenerRegistrationHolder;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchManager;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapper;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapperImpl;
+import org.opendaylight.openflowplugin.learningswitch.WakeupOnNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens to packetIn notification and
+ * <ul>
+ * <li>in HUB mode simply floods all switch ports (except ingress port)</li>
+ * <li>in LSWITCH mode collects source MAC address of packetIn and bind it with ingress port.
+ * If target MAC address is already bound then a flow is created (for direct communication between
+ * corresponding MACs)</li>
+ * </ul>
+ */
+public class LearningSwitchManagerMultiImpl implements DataChangeListenerRegistrationHolder,
+ LearningSwitchManager {
+
+ protected static final Logger LOG = LoggerFactory
+ .getLogger(LearningSwitchManagerMultiImpl.class);
+
+ private NotificationService notificationService;
+ private PacketProcessingService packetProcessingService;
+ private DataBrokerService data;
+
+ private Registration<NotificationListener> packetInRegistration;
+
+ private ListenerRegistration<DataChangeListener> dataChangeListenerRegistration;
+
+ /**
+ * @param notificationService the notificationService to set
+ */
+ @Override
+ public void setNotificationService(NotificationService notificationService) {
+ this.notificationService = notificationService;
+ }
+
+ /**
+ * @param packetProcessingService the packetProcessingService to set
+ */
+ @Override
+ public void setPacketProcessingService(
+ PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ /**
+ * @param data the data to set
+ */
+ @Override
+ public void setDataBroker(DataBrokerService data) {
+ this.data = data;
+ }
+
+ /**
+ * starting learning switch
+ */
+ @Override
+ public void start() {
+ LOG.debug("start() -->");
+ FlowCommitWrapper dataStoreAccessor = new FlowCommitWrapperImpl(data);
+
+ PacketInDispatcherImpl packetInDispatcher = new PacketInDispatcherImpl();
+ MultipleLearningSwitchHandlerFacadeImpl learningSwitchHandler = new MultipleLearningSwitchHandlerFacadeImpl();
+ learningSwitchHandler.setRegistrationPublisher(this);
+ learningSwitchHandler.setDataStoreAccessor(dataStoreAccessor);
+ learningSwitchHandler.setPacketProcessingService(packetProcessingService);
+ learningSwitchHandler.setPacketInDispatcher(packetInDispatcher);
+ packetInRegistration = notificationService.registerNotificationListener(packetInDispatcher);
+
+ WakeupOnNode wakeupListener = new WakeupOnNode();
+ wakeupListener.setLearningSwitchHandler(learningSwitchHandler);
+ dataChangeListenerRegistration = data.registerDataChangeListener(
+ InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class)
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class).toInstance(),
+ wakeupListener);
+ LOG.debug("start() <--");
+ }
+
+ /**
+ * stopping learning switch
+ */
+ @Override
+ public void stop() {
+ LOG.debug("stop() -->");
+ //TODO: remove flow (created in #start())
+ try {
+ packetInRegistration.close();
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ try {
+ dataChangeListenerRegistration.close();
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ LOG.debug("stop() <--");
+ }
+
+
+ @Override
+ public ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration() {
+ return dataChangeListenerRegistration;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.learningswitch.multi;
+
+import org.opendaylight.openflowplugin.learningswitch.DataChangeListenerRegistrationHolder;
+import org.opendaylight.openflowplugin.learningswitch.InstanceIdentifierUtils;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapper;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchHandler;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchHandlerSimpleImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class MultipleLearningSwitchHandlerFacadeImpl implements LearningSwitchHandler {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(MultipleLearningSwitchHandlerFacadeImpl.class);
+
+ private FlowCommitWrapper dataStoreAccessor;
+ private PacketProcessingService packetProcessingService;
+ private PacketInDispatcherImpl packetInDispatcher;
+
+ @Override
+ public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
+ LOG.debug("expected table acquired, learning ..");
+
+ /**
+ * appearedTablePath is in form of /nodes/node/node-id/table/table-id
+ * so we shorten it to /nodes/node/node-id to get identifier of switch.
+ *
+ */
+ InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(appearedTablePath);
+
+ /**
+ * We check if we already initialized dispatcher for that node,
+ * if not we create new handler for switch.
+ *
+ */
+ if (!packetInDispatcher.getHandlerMapping().containsKey(nodePath)) {
+ // delegate this node (owning appearedTable) to SimpleLearningSwitchHandler
+ LearningSwitchHandlerSimpleImpl simpleLearningSwitch = new LearningSwitchHandlerSimpleImpl();
+ /**
+ * We set runtime dependencies
+ */
+ simpleLearningSwitch.setDataStoreAccessor(dataStoreAccessor);
+ simpleLearningSwitch.setPacketProcessingService(packetProcessingService);
+
+ /**
+ * We propagate table event to newly instantiated instance of learning switch
+ */
+ simpleLearningSwitch.onSwitchAppeared(appearedTablePath);
+ /**
+ * We update mapping of already instantiated LearningSwitchHanlders
+ */
+ packetInDispatcher.getHandlerMapping().put(nodePath, simpleLearningSwitch);
+ }
+ }
+
+ @Override
+ public void setRegistrationPublisher(
+ DataChangeListenerRegistrationHolder registrationPublisher) {
+ //NOOP
+ }
+
+ @Override
+ public void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor) {
+ this.dataStoreAccessor = dataStoreAccessor;
+ }
+
+ @Override
+ public void setPacketProcessingService(
+ PacketProcessingService packetProcessingService) {
+ this.packetProcessingService = packetProcessingService;
+ }
+
+ /**
+ * @param packetInDispatcher
+ */
+ public void setPacketInDispatcher(PacketInDispatcherImpl packetInDispatcher) {
+ this.packetInDispatcher = packetInDispatcher;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.learningswitch.multi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class PacketInDispatcherImpl implements PacketProcessingListener {
+
+ private Map<InstanceIdentifier<Node>, PacketProcessingListener> handlerMapping;
+
+ /**
+ * default constructor
+ */
+ public PacketInDispatcherImpl() {
+ handlerMapping = new HashMap<>();
+ }
+
+ @Override
+ public void onPacketReceived(PacketReceived notification) {
+ // find corresponding handler
+ /**
+ * Notification contains reference to ingress port
+ * in a form of path in inventory: /nodes/node/node-connector
+ *
+ * In order to get path we shorten path to the first node reference
+ * by using firstIdentifierOf helper method provided by InstanceIdentifier,
+ * this will effectively shorten the path to /nodes/node.
+ *
+ */
+ InstanceIdentifier<?> ingressPort = notification.getIngress().getValue();
+ InstanceIdentifier<Node> nodeOfPacket = ingressPort.firstIdentifierOf(Node.class);
+ /**
+ * We lookup up the the packet-in listener for this node.
+ *
+ */
+ PacketProcessingListener nodeHandler = handlerMapping.get(nodeOfPacket);
+
+ /**
+ *
+ * If we have packet-processing listener, we delegate notification.
+ *
+ */
+ if (nodeHandler != null) {
+ nodeHandler.onPacketReceived(notification);
+ }
+ }
+
+ /**
+ * @return the handlerMapping
+ */
+ public Map<InstanceIdentifier<Node>, PacketProcessingListener> getHandlerMapping() {
+ return handlerMapping;
+ }
+}