--- /dev/null
+/*
+ * Copyright (c) 2013-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.controller.sal.implementation.internal;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
+import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The SAL Flow Programmer Service. It dispatches the flow programming requests
+ * to the proper SDN protocol plugin and it notifies about asynchronous messages
+ * received from the network node related to flow programming.
+ */
+public class FlowProgrammerService implements IFlowProgrammerService,
+ IPluginOutFlowProgrammerService, CommandProvider {
+
+ protected static final Logger logger = LoggerFactory
+ .getLogger(FlowProgrammerService.class);
+ private ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>> pluginFlowProgrammer;
+ private Set<IFlowProgrammerListener> listener;
+ private AtomicLong seq;
+
+ public FlowProgrammerService() {
+ pluginFlowProgrammer = new ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>>();
+ listener = new HashSet<IFlowProgrammerListener>();
+ seq = new AtomicLong();
+ /*
+ * This Request ID generator starts with 1. Each aysnc message is
+ * associated with an unique Request ID (!= 0).
+ */
+ seq.lazySet(1);
+ }
+
+ /**
+ * Function called by the dependency manager when all the required
+ * dependencies are satisfied
+ *
+ */
+ void init() {
+ logger.debug("INIT called!");
+ }
+
+ /**
+ * Function called by the dependency manager when at least one dependency
+ * become unsatisfied or when the component is shutting down because for
+ * example bundle is being stopped.
+ *
+ */
+ void destroy() {
+ // Clear previous registration to avoid they are left hanging
+ this.pluginFlowProgrammer.clear();
+ logger.debug("DESTROY called!");
+ }
+
+ /**
+ * Function called by dependency manager after "init ()" is called and after
+ * the services provided by the class are registered in the service registry
+ *
+ */
+ void start() {
+ logger.debug("START called!");
+ // OSGI console
+ registerWithOSGIConsole();
+ }
+
+ /**
+ * Function called by the dependency manager before the services exported by
+ * the component are unregistered, this will be followed by a "destroy ()"
+ * calls
+ *
+ */
+ void stop() {
+ logger.debug("STOP called!");
+ }
+
+ // Set the reference to the plugin flow programmer
+ public void setService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
+ ProtocolService.set(this.pluginFlowProgrammer, props, s, logger);
+ }
+
+ public void unsetService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
+ ProtocolService.unset(this.pluginFlowProgrammer, props, s, logger);
+ }
+
+ public void setListener(IFlowProgrammerListener s) {
+ this.listener.add(s);
+ }
+
+ public void unsetListener(IFlowProgrammerListener s) {
+ this.listener.remove(s);
+ }
+
+ @Override
+ public Status addFlow(Node node, Flow flow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().addFlow(node, flow);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status removeFlow(Node node, Flow flow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().removeFlow(node, flow);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status removeAllFlows(Node node) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().removeAllFlows(node);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().modifyFlow(node, oldFlow, newFlow);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status addFlowAsync(Node node, Flow flow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().addFlowAsync(node, flow, getNextRid());
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status removeFlowAsync(Node node, Flow flow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().removeFlowAsync(node, flow, getNextRid());
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
+ if (pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public void flowRemoved(Node node, Flow flow) {
+ for (IFlowProgrammerListener l : listener) {
+ l.flowRemoved(node, flow);
+ }
+ }
+
+ @Override
+ public void flowErrorReported(Node node, long rid, Object err) {
+ logger.error("Got error {} for message rid {} from node {}",
+ new Object[] { err, rid, node });
+
+ for (IFlowProgrammerListener l : listener) {
+ l.flowErrorReported(node, rid, err);
+ }
+ }
+
+ // ---------------- OSGI TEST CODE ------------------------------//
+
+ private void registerWithOSGIConsole() {
+ BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+ .getBundleContext();
+ bundleContext.registerService(CommandProvider.class.getName(), this,
+ null);
+ }
+
+ @Override
+ public String getHelp() {
+ StringBuffer help = new StringBuffer();
+ help.append("---SAL Flow Programmer testing commands---\n");
+ help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
+ help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
+ return help.toString();
+ }
+
+ public void _addflow(CommandInterpreter ci) throws UnknownHostException {
+ Node node = null;
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node id not specified");
+ return;
+ }
+ try {
+ node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+ } catch (NumberFormatException e) {
+ logger.error("",e);
+ } catch (ConstructionException e) {
+ logger.error("",e);
+ }
+ ci.println(this.addFlow(node, getSampleFlow(node)));
+ }
+
+ public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
+ Node node = null;
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node id not specified");
+ return;
+ }
+ try {
+ node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+ } catch (NumberFormatException e) {
+ logger.error("",e);
+ } catch (ConstructionException e) {
+ logger.error("",e);
+ }
+ Flow flowA = getSampleFlow(node);
+ Flow flowB = getSampleFlow(node);
+ Match matchB = flowB.getMatch();
+ matchB.setField(MatchType.NW_DST,
+ InetAddress.getByName("190.190.190.190"));
+ flowB.setMatch(matchB);
+ ci.println(this.modifyFlow(node, flowA, flowB));
+ }
+
+ public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
+ Node node = null;
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node id not specified");
+ return;
+ }
+ try {
+ node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+ } catch (NumberFormatException e) {
+ logger.error("",e);
+ } catch (ConstructionException e) {
+ logger.error("",e);
+ }
+ ci.println(this.removeFlow(node, getSampleFlow(node)));
+ }
+
+ public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
+ Node node = null;
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node id not specified");
+ return;
+ }
+ try {
+ node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+ } catch (NumberFormatException e) {
+ logger.error("",e);
+ } catch (ConstructionException e) {
+ logger.error("",e);
+ }
+ ci.println(this.addFlow(node, getSampleFlowV6(node)));
+ }
+
+ public void _removeflowv6(CommandInterpreter ci)
+ throws UnknownHostException {
+ Node node = null;
+ String nodeId = ci.nextArgument();
+ if (nodeId == null) {
+ ci.print("Node id not specified");
+ return;
+ }
+ try {
+ node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+ } catch (NumberFormatException e) {
+ logger.error("",e);
+ } catch (ConstructionException e) {
+ logger.error("",e);
+ }
+ ci.println(this.removeFlow(node, getSampleFlowV6(node)));
+ }
+
+ private Flow getSampleFlow(Node node) throws UnknownHostException {
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
+ (short) 24, node);
+ NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
+ (short) 30, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+ (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+ (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress.getByName("172.28.30.50");
+ InetAddress dstIP = InetAddress.getByName("171.71.9.52");
+ InetAddress newIP = InetAddress.getByName("200.200.100.1");
+ InetAddress ipMask = InetAddress.getByName("255.255.255.0");
+ InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
+ short ethertype = EtherTypes.IPv4.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.TCP.byteValue();
+ short src = (short) 55000;
+ short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(MatchType.IN_PORT, port);
+ match.setField(MatchType.DL_SRC, srcMac);
+ match.setField(MatchType.DL_DST, dstMac);
+ match.setField(MatchType.DL_TYPE, ethertype);
+ match.setField(MatchType.DL_VLAN, vlan);
+ match.setField(MatchType.DL_VLAN_PR, vlanPr);
+ match.setField(MatchType.NW_SRC, srcIP, ipMask);
+ match.setField(MatchType.NW_DST, dstIP, ipMask2);
+ match.setField(MatchType.NW_TOS, tos);
+ match.setField(MatchType.NW_PROTO, proto);
+ match.setField(MatchType.TP_SRC, src);
+ match.setField(MatchType.TP_DST, dst);
+
+ List<Action> actions = new ArrayList<Action>();
+ actions.add(new SetNwDst(newIP));
+ actions.add(new Output(oport));
+ actions.add(new PopVlan());
+ actions.add(new Flood());
+ actions.add(new Controller());
+
+ Flow flow = new Flow(match, actions);
+ flow.setPriority((short) 100);
+ flow.setHardTimeout((short) 360);
+ flow.setId(1234L);
+
+ return flow;
+ }
+
+ private Flow getSampleFlowV6(Node node) throws UnknownHostException {
+ NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
+ (short) 24, node);
+ NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
+ (short) 30, node);
+ byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+ (byte) 0x9a, (byte) 0xbc };
+ byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+ (byte) 0x5e, (byte) 0x6f };
+ InetAddress srcIP = InetAddress
+ .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+ InetAddress dstIP = InetAddress
+ .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+ InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+ // V6Match implementation assumes no mask is
+ // specified
+ InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+ short ethertype = EtherTypes.IPv6.shortValue();
+ short vlan = (short) 27;
+ byte vlanPr = (byte) 3;
+ Byte tos = 4;
+ byte proto = IPProtocols.UDP.byteValue();
+ short src = (short) 5500;
+ // short dst = 80;
+
+ /*
+ * Create a SAL Flow aFlow
+ */
+ Match match = new Match();
+ match.setField(MatchType.IN_PORT, port);
+ match.setField(MatchType.DL_SRC, srcMac);
+ match.setField(MatchType.DL_DST, dstMac);
+ match.setField(MatchType.DL_TYPE, ethertype);
+ match.setField(MatchType.DL_VLAN, vlan);
+ match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
+ // this properly...
+ match.setField(MatchType.NW_SRC, srcIP, ipMask);
+ match.setField(MatchType.NW_DST, dstIP, ipMask2);
+ match.setField(MatchType.NW_TOS, tos);
+ match.setField(MatchType.NW_PROTO, proto);
+ match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
+ // properly...
+ // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
+ // properly...
+
+ List<Action> actions = new ArrayList<Action>();
+ actions.add(new Output(oport));
+ actions.add(new PopVlan());
+ actions.add(new Flood());
+
+ Flow flow = new Flow(match, actions);
+ flow.setPriority((short) 300);
+ flow.setHardTimeout((short) 240);
+ flow.setId(65536L);
+
+ return flow;
+ }
+
+ /**
+ * This Request ID generator starts with 1. Each aysnc message is
+ * associated with an unique Request ID (!= 0).
+ *
+ * @return Request ID
+ */
+ private long getNextRid() {
+ return seq.getAndIncrement();
+ }
+
+ @Override
+ public Status syncSendBarrierMessage(Node node) {
+ if (this.pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().syncSendBarrierMessage(node);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+
+ @Override
+ public Status asyncSendBarrierMessage(Node node) {
+ if (this.pluginFlowProgrammer != null) {
+ ProtocolService<IPluginInFlowProgrammerService> service =
+ this.pluginFlowProgrammer.get(node.getType());
+ if (service != null) {
+ return service.getService().asyncSendBarrierMessage(node);
+ }
+ }
+ return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+ }
+}