2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.implementation.internal;
12 import java.util.concurrent.ConcurrentHashMap;
13 import java.util.concurrent.atomic.AtomicLong;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.List;
21 import org.eclipse.osgi.framework.console.CommandInterpreter;
22 import org.eclipse.osgi.framework.console.CommandProvider;
23 import org.opendaylight.controller.sal.action.Action;
24 import org.opendaylight.controller.sal.action.Controller;
25 import org.opendaylight.controller.sal.action.Flood;
26 import org.opendaylight.controller.sal.action.Output;
27 import org.opendaylight.controller.sal.action.PopVlan;
28 import org.opendaylight.controller.sal.action.SetNwDst;
29 import org.opendaylight.controller.sal.core.ConstructionException;
30 import org.opendaylight.controller.sal.core.Node;
31 import org.opendaylight.controller.sal.core.Node.NodeIDType;
32 import org.opendaylight.controller.sal.core.NodeConnector;
33 import org.opendaylight.controller.sal.flowprogrammer.Flow;
34 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
35 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
36 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
37 import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
38 import org.opendaylight.controller.sal.match.Match;
39 import org.opendaylight.controller.sal.match.MatchType;
40 import org.opendaylight.controller.sal.utils.EtherTypes;
41 import org.opendaylight.controller.sal.utils.IPProtocols;
42 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
43 import org.opendaylight.controller.sal.utils.Status;
44 import org.opendaylight.controller.sal.utils.StatusCode;
45 import org.osgi.framework.BundleContext;
46 import org.osgi.framework.FrameworkUtil;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
51 * The SAL Flow Programmer Service. It dispatches the flow programming requests
52 * to the proper SDN protocol plugin and it notifies about asynchronous messages
53 * received from the network node related to flow programming.
55 public class FlowProgrammerService implements IFlowProgrammerService,
56 IPluginOutFlowProgrammerService, CommandProvider {
58 protected static final Logger logger = LoggerFactory
59 .getLogger(FlowProgrammerService.class);
60 private ConcurrentHashMap<String, IPluginInFlowProgrammerService> pluginFlowProgrammer;
61 private Set<IFlowProgrammerListener> listener;
62 private AtomicLong seq;
64 public FlowProgrammerService() {
65 pluginFlowProgrammer = new ConcurrentHashMap<String, IPluginInFlowProgrammerService>();
66 listener = new HashSet<IFlowProgrammerListener>();
67 seq = new AtomicLong();
69 * This Request ID generator starts with 1. Each aysnc message is
70 * associated with an unique Request ID (!= 0).
76 * Function called by the dependency manager when all the required
77 * dependencies are satisfied
81 logger.debug("INIT called!");
85 * Function called by the dependency manager when at least one dependency
86 * become unsatisfied or when the component is shutting down because for
87 * example bundle is being stopped.
91 // Clear previous registration to avoid they are left hanging
92 this.pluginFlowProgrammer.clear();
93 logger.debug("DESTROY called!");
97 * Function called by dependency manager after "init ()" is called and after
98 * the services provided by the class are registered in the service registry
102 logger.debug("START called!");
104 registerWithOSGIConsole();
108 * Function called by the dependency manager before the services exported by
109 * the component are unregistered, this will be followed by a "destroy ()"
114 logger.debug("STOP called!");
117 // Set the reference to the plugin flow programmer
118 public void setService(Map props, IPluginInFlowProgrammerService s) {
119 if (this.pluginFlowProgrammer == null) {
120 logger.error("pluginFlowProgrammer store null");
124 logger.trace("Got a service set request {}", s);
126 for (Object e : props.entrySet()) {
127 Map.Entry entry = (Map.Entry) e;
128 logger.trace("Prop key:({}) value:({})", entry.getKey(),
132 Object value = props.get("protocolPluginType");
133 if (value instanceof String) {
134 type = (String) value;
137 logger.error("Received a pluginFlowProgrammer without any "
138 + "protocolPluginType provided");
140 this.pluginFlowProgrammer.put(type, s);
141 logger.debug("Stored the pluginFlowProgrammer for type: {}", type);
145 public void unsetService(Map props, IPluginInFlowProgrammerService s) {
146 if (this.pluginFlowProgrammer == null) {
147 logger.error("pluginFlowProgrammer store null");
152 logger.debug("Received unsetpluginFlowProgrammer request");
153 for (Object e : props.entrySet()) {
154 Map.Entry entry = (Map.Entry) e;
155 logger.trace("Prop key:({}) value:({})", entry.getKey(),
159 Object value = props.get("protocoloPluginType");
160 if (value instanceof String) {
161 type = (String) value;
164 logger.error("Received a pluginFlowProgrammer without any "
165 + "protocolPluginType provided");
166 } else if (this.pluginFlowProgrammer.get(type).equals(s)) {
167 this.pluginFlowProgrammer.remove(type);
168 logger.debug("Removed the pluginFlowProgrammer for type: {}", type);
172 public void setListener(IFlowProgrammerListener s) {
173 this.listener.add(s);
176 public void unsetListener(IFlowProgrammerListener s) {
177 this.listener.remove(s);
181 public Status addFlow(Node node, Flow flow) {
182 if (pluginFlowProgrammer != null) {
183 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
184 return this.pluginFlowProgrammer.get(node.getType()).addFlow(
188 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
192 public Status removeFlow(Node node, Flow flow) {
193 if (pluginFlowProgrammer != null) {
194 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
195 return this.pluginFlowProgrammer.get(node.getType())
196 .removeFlow(node, flow);
199 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
203 public Status removeAllFlows(Node node) {
204 if (pluginFlowProgrammer != null) {
205 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
206 return this.pluginFlowProgrammer.get(node.getType())
207 .removeAllFlows(node);
210 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
214 public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
215 if (pluginFlowProgrammer != null) {
216 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
217 return this.pluginFlowProgrammer.get(node.getType())
218 .modifyFlow(node, oldFlow, newFlow);
221 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
225 public Status addFlowAsync(Node node, Flow flow) {
226 if (pluginFlowProgrammer != null) {
227 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
228 return this.pluginFlowProgrammer.get(node.getType()).addFlowAsync(
229 node, flow, getNextRid());
232 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
236 public Status removeFlowAsync(Node node, Flow flow) {
237 if (pluginFlowProgrammer != null) {
238 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
239 return this.pluginFlowProgrammer.get(node.getType())
240 .removeFlowAsync(node, flow, getNextRid());
243 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
247 public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
248 if (pluginFlowProgrammer != null) {
249 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
250 return this.pluginFlowProgrammer.get(node.getType())
251 .modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
254 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
258 public void flowRemoved(Node node, Flow flow) {
259 for (IFlowProgrammerListener l : listener) {
260 l.flowRemoved(node, flow);
265 public void flowErrorReported(Node node, long rid, Object err) {
266 logger.error("Got error {} for message rid {} from node {}",
267 new Object[] { err, rid, node });
269 for (IFlowProgrammerListener l : listener) {
270 l.flowErrorReported(node, rid, err);
274 // ---------------- OSGI TEST CODE ------------------------------//
276 private void registerWithOSGIConsole() {
277 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
279 bundleContext.registerService(CommandProvider.class.getName(), this,
284 public String getHelp() {
285 StringBuffer help = new StringBuffer();
286 help.append("---SAL Flow Programmer testing commands---\n");
287 help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
288 help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
289 return help.toString();
292 public void _addflow(CommandInterpreter ci) throws UnknownHostException {
294 String nodeId = ci.nextArgument();
295 if (nodeId == null) {
296 ci.print("Node id not specified");
300 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
301 } catch (NumberFormatException e) {
303 } catch (ConstructionException e) {
306 ci.println(this.addFlow(node, getSampleFlow(node)));
309 public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
311 String nodeId = ci.nextArgument();
312 if (nodeId == null) {
313 ci.print("Node id not specified");
317 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
318 } catch (NumberFormatException e) {
320 } catch (ConstructionException e) {
323 Flow flowA = getSampleFlow(node);
324 Flow flowB = getSampleFlow(node);
325 Match matchB = flowB.getMatch();
326 matchB.setField(MatchType.NW_DST,
327 InetAddress.getByName("190.190.190.190"));
328 flowB.setMatch(matchB);
329 ci.println(this.modifyFlow(node, flowA, flowB));
332 public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
334 String nodeId = ci.nextArgument();
335 if (nodeId == null) {
336 ci.print("Node id not specified");
340 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
341 } catch (NumberFormatException e) {
343 } catch (ConstructionException e) {
346 ci.println(this.removeFlow(node, getSampleFlow(node)));
349 public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
351 String nodeId = ci.nextArgument();
352 if (nodeId == null) {
353 ci.print("Node id not specified");
357 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
358 } catch (NumberFormatException e) {
360 } catch (ConstructionException e) {
363 ci.println(this.addFlow(node, getSampleFlowV6(node)));
366 public void _removeflowv6(CommandInterpreter ci)
367 throws UnknownHostException {
369 String nodeId = ci.nextArgument();
370 if (nodeId == null) {
371 ci.print("Node id not specified");
375 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
376 } catch (NumberFormatException e) {
378 } catch (ConstructionException e) {
381 ci.println(this.removeFlow(node, getSampleFlowV6(node)));
384 private Flow getSampleFlow(Node node) throws UnknownHostException {
385 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
387 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
389 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
390 (byte) 0x9a, (byte) 0xbc };
391 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
392 (byte) 0x5e, (byte) 0x6f };
393 InetAddress srcIP = InetAddress.getByName("172.28.30.50");
394 InetAddress dstIP = InetAddress.getByName("171.71.9.52");
395 InetAddress newIP = InetAddress.getByName("200.200.100.1");
396 InetAddress ipMask = InetAddress.getByName("255.255.255.0");
397 InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
398 short ethertype = EtherTypes.IPv4.shortValue();
399 short vlan = (short) 27;
402 byte proto = IPProtocols.TCP.byteValue();
403 short src = (short) 55000;
407 * Create a SAL Flow aFlow
409 Match match = new Match();
410 match.setField(MatchType.IN_PORT, port);
411 match.setField(MatchType.DL_SRC, srcMac);
412 match.setField(MatchType.DL_DST, dstMac);
413 match.setField(MatchType.DL_TYPE, ethertype);
414 match.setField(MatchType.DL_VLAN, vlan);
415 match.setField(MatchType.DL_VLAN_PR, vlanPr);
416 match.setField(MatchType.NW_SRC, srcIP, ipMask);
417 match.setField(MatchType.NW_DST, dstIP, ipMask2);
418 match.setField(MatchType.NW_TOS, tos);
419 match.setField(MatchType.NW_PROTO, proto);
420 match.setField(MatchType.TP_SRC, src);
421 match.setField(MatchType.TP_DST, dst);
423 List<Action> actions = new ArrayList<Action>();
424 actions.add(new SetNwDst(newIP));
425 actions.add(new Output(oport));
426 actions.add(new PopVlan());
427 actions.add(new Flood());
428 actions.add(new Controller());
430 Flow flow = new Flow(match, actions);
431 flow.setPriority((short) 100);
432 flow.setHardTimeout((short) 360);
437 private Flow getSampleFlowV6(Node node) throws UnknownHostException {
438 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
440 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
442 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
443 (byte) 0x9a, (byte) 0xbc };
444 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
445 (byte) 0x5e, (byte) 0x6f };
446 InetAddress srcIP = InetAddress
447 .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
448 InetAddress dstIP = InetAddress
449 .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
450 InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
451 // V6Match implementation assumes no mask is
453 InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
454 short ethertype = EtherTypes.IPv6.shortValue();
455 short vlan = (short) 27;
456 byte vlanPr = (byte) 3;
458 byte proto = IPProtocols.UDP.byteValue();
459 short src = (short) 5500;
463 * Create a SAL Flow aFlow
465 Match match = new Match();
466 match.setField(MatchType.IN_PORT, port);
467 match.setField(MatchType.DL_SRC, srcMac);
468 match.setField(MatchType.DL_DST, dstMac);
469 match.setField(MatchType.DL_TYPE, ethertype);
470 match.setField(MatchType.DL_VLAN, vlan);
471 match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
473 match.setField(MatchType.NW_SRC, srcIP, ipMask);
474 match.setField(MatchType.NW_DST, dstIP, ipMask2);
475 match.setField(MatchType.NW_TOS, tos);
476 match.setField(MatchType.NW_PROTO, proto);
477 match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
479 // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
482 List<Action> actions = new ArrayList<Action>();
483 actions.add(new Output(oport));
484 actions.add(new PopVlan());
485 actions.add(new Flood());
487 Flow flow = new Flow(match, actions);
488 flow.setPriority((short) 300);
489 flow.setHardTimeout((short) 240);
495 * This Request ID generator starts with 1. Each aysnc message is
496 * associated with an unique Request ID (!= 0).
500 private long getNextRid() {
501 return seq.getAndIncrement();
505 public Status sendBarrierMessage(Node node) {
506 if (this.pluginFlowProgrammer != null) {
507 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
508 return this.pluginFlowProgrammer.get(node.getType())
509 .sendBarrierMessage(node);
512 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");