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.GlobalConstants;
42 import org.opendaylight.controller.sal.utils.IPProtocols;
43 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
44 import org.opendaylight.controller.sal.utils.Status;
45 import org.opendaylight.controller.sal.utils.StatusCode;
46 import org.osgi.framework.BundleContext;
47 import org.osgi.framework.FrameworkUtil;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
52 * The SAL Flow Programmer Service. It dispatches the flow programming requests
53 * to the proper SDN protocol plugin and it notifies about asynchronous messages
54 * received from the network node related to flow programming.
56 public class FlowProgrammerService implements IFlowProgrammerService,
57 IPluginOutFlowProgrammerService, CommandProvider {
59 protected static final Logger logger = LoggerFactory
60 .getLogger(FlowProgrammerService.class);
61 private ConcurrentHashMap<String, IPluginInFlowProgrammerService> pluginFlowProgrammer;
62 private Set<IFlowProgrammerListener> listener;
63 private AtomicLong seq;
65 public FlowProgrammerService() {
66 pluginFlowProgrammer = new ConcurrentHashMap<String, IPluginInFlowProgrammerService>();
67 listener = new HashSet<IFlowProgrammerListener>();
68 seq = new AtomicLong();
70 * This Request ID generator starts with 1. Each aysnc message is
71 * associated with an unique Request ID (!= 0).
77 * Function called by the dependency manager when all the required
78 * dependencies are satisfied
82 logger.debug("INIT called!");
86 * Function called by the dependency manager when at least one dependency
87 * become unsatisfied or when the component is shutting down because for
88 * example bundle is being stopped.
92 // Clear previous registration to avoid they are left hanging
93 this.pluginFlowProgrammer.clear();
94 logger.debug("DESTROY called!");
98 * Function called by dependency manager after "init ()" is called and after
99 * the services provided by the class are registered in the service registry
103 logger.debug("START called!");
105 registerWithOSGIConsole();
109 * Function called by the dependency manager before the services exported by
110 * the component are unregistered, this will be followed by a "destroy ()"
115 logger.debug("STOP called!");
118 // Set the reference to the plugin flow programmer
119 public void setService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
120 if (this.pluginFlowProgrammer == null) {
121 logger.error("pluginFlowProgrammer store null");
125 if (logger.isTraceEnabled()) {
126 logger.trace("Got a service set request {}", s);
127 for (Map.Entry<String, Object> entry : props.entrySet()) {
128 logger.trace("Prop key:({}) value:({})", entry.getKey(), entry.getValue());
133 Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
134 if (value instanceof String) {
135 type = (String) value;
138 logger.error("Received a pluginFlowProgrammer without any "
139 + "protocolPluginType provided");
141 this.pluginFlowProgrammer.put(type, s);
142 logger.debug("Stored the pluginFlowProgrammer for type: {}", type);
146 public void unsetService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
147 if (this.pluginFlowProgrammer == null) {
148 logger.error("pluginFlowProgrammer store null");
152 logger.debug("Received unsetpluginFlowProgrammer request");
153 if (logger.isTraceEnabled()) {
154 logger.trace("Got a service set request {}", s);
155 for (Map.Entry<String, Object> entry : props.entrySet()) {
156 logger.trace("Prop key:({}) value:({})", entry.getKey(), entry.getValue());
161 Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
162 if (value instanceof String) {
163 type = (String) value;
166 logger.error("Received a pluginFlowProgrammer without any "
167 + "protocolPluginType provided");
168 } else if (this.pluginFlowProgrammer.get(type).equals(s)) {
169 this.pluginFlowProgrammer.remove(type);
170 logger.debug("Removed the pluginFlowProgrammer for type: {}", type);
174 public void setListener(IFlowProgrammerListener s) {
175 this.listener.add(s);
178 public void unsetListener(IFlowProgrammerListener s) {
179 this.listener.remove(s);
183 public Status addFlow(Node node, Flow flow) {
184 if (pluginFlowProgrammer != null) {
185 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
186 return this.pluginFlowProgrammer.get(node.getType()).addFlow(
190 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
194 public Status removeFlow(Node node, Flow flow) {
195 if (pluginFlowProgrammer != null) {
196 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
197 return this.pluginFlowProgrammer.get(node.getType())
198 .removeFlow(node, flow);
201 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
205 public Status removeAllFlows(Node node) {
206 if (pluginFlowProgrammer != null) {
207 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
208 return this.pluginFlowProgrammer.get(node.getType())
209 .removeAllFlows(node);
212 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
216 public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
217 if (pluginFlowProgrammer != null) {
218 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
219 return this.pluginFlowProgrammer.get(node.getType())
220 .modifyFlow(node, oldFlow, newFlow);
223 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
227 public Status addFlowAsync(Node node, Flow flow) {
228 if (pluginFlowProgrammer != null) {
229 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
230 return this.pluginFlowProgrammer.get(node.getType()).addFlowAsync(
231 node, flow, getNextRid());
234 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
238 public Status removeFlowAsync(Node node, Flow flow) {
239 if (pluginFlowProgrammer != null) {
240 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
241 return this.pluginFlowProgrammer.get(node.getType())
242 .removeFlowAsync(node, flow, getNextRid());
245 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
249 public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
250 if (pluginFlowProgrammer != null) {
251 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
252 return this.pluginFlowProgrammer.get(node.getType())
253 .modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
256 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
260 public void flowRemoved(Node node, Flow flow) {
261 for (IFlowProgrammerListener l : listener) {
262 l.flowRemoved(node, flow);
267 public void flowErrorReported(Node node, long rid, Object err) {
268 logger.error("Got error {} for message rid {} from node {}",
269 new Object[] { err, rid, node });
271 for (IFlowProgrammerListener l : listener) {
272 l.flowErrorReported(node, rid, err);
276 // ---------------- OSGI TEST CODE ------------------------------//
278 private void registerWithOSGIConsole() {
279 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
281 bundleContext.registerService(CommandProvider.class.getName(), this,
286 public String getHelp() {
287 StringBuffer help = new StringBuffer();
288 help.append("---SAL Flow Programmer testing commands---\n");
289 help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
290 help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
291 return help.toString();
294 public void _addflow(CommandInterpreter ci) throws UnknownHostException {
296 String nodeId = ci.nextArgument();
297 if (nodeId == null) {
298 ci.print("Node id not specified");
302 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
303 } catch (NumberFormatException e) {
305 } catch (ConstructionException e) {
308 ci.println(this.addFlow(node, getSampleFlow(node)));
311 public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
313 String nodeId = ci.nextArgument();
314 if (nodeId == null) {
315 ci.print("Node id not specified");
319 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
320 } catch (NumberFormatException e) {
322 } catch (ConstructionException e) {
325 Flow flowA = getSampleFlow(node);
326 Flow flowB = getSampleFlow(node);
327 Match matchB = flowB.getMatch();
328 matchB.setField(MatchType.NW_DST,
329 InetAddress.getByName("190.190.190.190"));
330 flowB.setMatch(matchB);
331 ci.println(this.modifyFlow(node, flowA, flowB));
334 public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
336 String nodeId = ci.nextArgument();
337 if (nodeId == null) {
338 ci.print("Node id not specified");
342 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
343 } catch (NumberFormatException e) {
345 } catch (ConstructionException e) {
348 ci.println(this.removeFlow(node, getSampleFlow(node)));
351 public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
353 String nodeId = ci.nextArgument();
354 if (nodeId == null) {
355 ci.print("Node id not specified");
359 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
360 } catch (NumberFormatException e) {
362 } catch (ConstructionException e) {
365 ci.println(this.addFlow(node, getSampleFlowV6(node)));
368 public void _removeflowv6(CommandInterpreter ci)
369 throws UnknownHostException {
371 String nodeId = ci.nextArgument();
372 if (nodeId == null) {
373 ci.print("Node id not specified");
377 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
378 } catch (NumberFormatException e) {
380 } catch (ConstructionException e) {
383 ci.println(this.removeFlow(node, getSampleFlowV6(node)));
386 private Flow getSampleFlow(Node node) throws UnknownHostException {
387 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
389 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
391 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
392 (byte) 0x9a, (byte) 0xbc };
393 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
394 (byte) 0x5e, (byte) 0x6f };
395 InetAddress srcIP = InetAddress.getByName("172.28.30.50");
396 InetAddress dstIP = InetAddress.getByName("171.71.9.52");
397 InetAddress newIP = InetAddress.getByName("200.200.100.1");
398 InetAddress ipMask = InetAddress.getByName("255.255.255.0");
399 InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
400 short ethertype = EtherTypes.IPv4.shortValue();
401 short vlan = (short) 27;
404 byte proto = IPProtocols.TCP.byteValue();
405 short src = (short) 55000;
409 * Create a SAL Flow aFlow
411 Match match = new Match();
412 match.setField(MatchType.IN_PORT, port);
413 match.setField(MatchType.DL_SRC, srcMac);
414 match.setField(MatchType.DL_DST, dstMac);
415 match.setField(MatchType.DL_TYPE, ethertype);
416 match.setField(MatchType.DL_VLAN, vlan);
417 match.setField(MatchType.DL_VLAN_PR, vlanPr);
418 match.setField(MatchType.NW_SRC, srcIP, ipMask);
419 match.setField(MatchType.NW_DST, dstIP, ipMask2);
420 match.setField(MatchType.NW_TOS, tos);
421 match.setField(MatchType.NW_PROTO, proto);
422 match.setField(MatchType.TP_SRC, src);
423 match.setField(MatchType.TP_DST, dst);
425 List<Action> actions = new ArrayList<Action>();
426 actions.add(new SetNwDst(newIP));
427 actions.add(new Output(oport));
428 actions.add(new PopVlan());
429 actions.add(new Flood());
430 actions.add(new Controller());
432 Flow flow = new Flow(match, actions);
433 flow.setPriority((short) 100);
434 flow.setHardTimeout((short) 360);
439 private Flow getSampleFlowV6(Node node) throws UnknownHostException {
440 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
442 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
444 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
445 (byte) 0x9a, (byte) 0xbc };
446 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
447 (byte) 0x5e, (byte) 0x6f };
448 InetAddress srcIP = InetAddress
449 .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
450 InetAddress dstIP = InetAddress
451 .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
452 InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
453 // V6Match implementation assumes no mask is
455 InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
456 short ethertype = EtherTypes.IPv6.shortValue();
457 short vlan = (short) 27;
458 byte vlanPr = (byte) 3;
460 byte proto = IPProtocols.UDP.byteValue();
461 short src = (short) 5500;
465 * Create a SAL Flow aFlow
467 Match match = new Match();
468 match.setField(MatchType.IN_PORT, port);
469 match.setField(MatchType.DL_SRC, srcMac);
470 match.setField(MatchType.DL_DST, dstMac);
471 match.setField(MatchType.DL_TYPE, ethertype);
472 match.setField(MatchType.DL_VLAN, vlan);
473 match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
475 match.setField(MatchType.NW_SRC, srcIP, ipMask);
476 match.setField(MatchType.NW_DST, dstIP, ipMask2);
477 match.setField(MatchType.NW_TOS, tos);
478 match.setField(MatchType.NW_PROTO, proto);
479 match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
481 // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
484 List<Action> actions = new ArrayList<Action>();
485 actions.add(new Output(oport));
486 actions.add(new PopVlan());
487 actions.add(new Flood());
489 Flow flow = new Flow(match, actions);
490 flow.setPriority((short) 300);
491 flow.setHardTimeout((short) 240);
497 * This Request ID generator starts with 1. Each aysnc message is
498 * associated with an unique Request ID (!= 0).
502 private long getNextRid() {
503 return seq.getAndIncrement();
507 public Status syncSendBarrierMessage(Node node) {
508 if (this.pluginFlowProgrammer != null) {
509 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
510 return this.pluginFlowProgrammer.get(node.getType())
511 .syncSendBarrierMessage(node);
514 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
518 public Status asyncSendBarrierMessage(Node node) {
519 if (this.pluginFlowProgrammer != null) {
520 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
521 return this.pluginFlowProgrammer.get(node.getType())
522 .asyncSendBarrierMessage(node);
525 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");