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 props, IPluginInFlowProgrammerService s) {
120 if (this.pluginFlowProgrammer == null) {
121 logger.error("pluginFlowProgrammer store null");
125 logger.trace("Got a service set request {}", s);
127 for (Object e : props.entrySet()) {
128 Map.Entry entry = (Map.Entry) e;
129 logger.trace("Prop key:({}) value:({})", entry.getKey(),
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 props, IPluginInFlowProgrammerService s) {
147 if (this.pluginFlowProgrammer == null) {
148 logger.error("pluginFlowProgrammer store null");
153 logger.debug("Received unsetpluginFlowProgrammer request");
154 for (Object e : props.entrySet()) {
155 Map.Entry entry = (Map.Entry) e;
156 logger.trace("Prop key:({}) value:({})", entry.getKey(),
160 Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
161 if (value instanceof String) {
162 type = (String) value;
165 logger.error("Received a pluginFlowProgrammer without any "
166 + "protocolPluginType provided");
167 } else if (this.pluginFlowProgrammer.get(type).equals(s)) {
168 this.pluginFlowProgrammer.remove(type);
169 logger.debug("Removed the pluginFlowProgrammer for type: {}", type);
173 public void setListener(IFlowProgrammerListener s) {
174 this.listener.add(s);
177 public void unsetListener(IFlowProgrammerListener s) {
178 this.listener.remove(s);
182 public Status addFlow(Node node, Flow flow) {
183 if (pluginFlowProgrammer != null) {
184 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
185 return this.pluginFlowProgrammer.get(node.getType()).addFlow(
189 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
193 public Status removeFlow(Node node, Flow flow) {
194 if (pluginFlowProgrammer != null) {
195 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
196 return this.pluginFlowProgrammer.get(node.getType())
197 .removeFlow(node, flow);
200 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
204 public Status removeAllFlows(Node node) {
205 if (pluginFlowProgrammer != null) {
206 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
207 return this.pluginFlowProgrammer.get(node.getType())
208 .removeAllFlows(node);
211 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
215 public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
216 if (pluginFlowProgrammer != null) {
217 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
218 return this.pluginFlowProgrammer.get(node.getType())
219 .modifyFlow(node, oldFlow, newFlow);
222 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
226 public Status addFlowAsync(Node node, Flow flow) {
227 if (pluginFlowProgrammer != null) {
228 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
229 return this.pluginFlowProgrammer.get(node.getType()).addFlowAsync(
230 node, flow, getNextRid());
233 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
237 public Status removeFlowAsync(Node node, Flow flow) {
238 if (pluginFlowProgrammer != null) {
239 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
240 return this.pluginFlowProgrammer.get(node.getType())
241 .removeFlowAsync(node, flow, getNextRid());
244 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
248 public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
249 if (pluginFlowProgrammer != null) {
250 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
251 return this.pluginFlowProgrammer.get(node.getType())
252 .modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
255 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
259 public void flowRemoved(Node node, Flow flow) {
260 for (IFlowProgrammerListener l : listener) {
261 l.flowRemoved(node, flow);
266 public void flowErrorReported(Node node, long rid, Object err) {
267 logger.error("Got error {} for message rid {} from node {}",
268 new Object[] { err, rid, node });
270 for (IFlowProgrammerListener l : listener) {
271 l.flowErrorReported(node, rid, err);
275 // ---------------- OSGI TEST CODE ------------------------------//
277 private void registerWithOSGIConsole() {
278 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
280 bundleContext.registerService(CommandProvider.class.getName(), this,
285 public String getHelp() {
286 StringBuffer help = new StringBuffer();
287 help.append("---SAL Flow Programmer testing commands---\n");
288 help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
289 help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
290 return help.toString();
293 public void _addflow(CommandInterpreter ci) throws UnknownHostException {
295 String nodeId = ci.nextArgument();
296 if (nodeId == null) {
297 ci.print("Node id not specified");
301 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
302 } catch (NumberFormatException e) {
304 } catch (ConstructionException e) {
307 ci.println(this.addFlow(node, getSampleFlow(node)));
310 public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
312 String nodeId = ci.nextArgument();
313 if (nodeId == null) {
314 ci.print("Node id not specified");
318 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
319 } catch (NumberFormatException e) {
321 } catch (ConstructionException e) {
324 Flow flowA = getSampleFlow(node);
325 Flow flowB = getSampleFlow(node);
326 Match matchB = flowB.getMatch();
327 matchB.setField(MatchType.NW_DST,
328 InetAddress.getByName("190.190.190.190"));
329 flowB.setMatch(matchB);
330 ci.println(this.modifyFlow(node, flowA, flowB));
333 public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
335 String nodeId = ci.nextArgument();
336 if (nodeId == null) {
337 ci.print("Node id not specified");
341 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
342 } catch (NumberFormatException e) {
344 } catch (ConstructionException e) {
347 ci.println(this.removeFlow(node, getSampleFlow(node)));
350 public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
352 String nodeId = ci.nextArgument();
353 if (nodeId == null) {
354 ci.print("Node id not specified");
358 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
359 } catch (NumberFormatException e) {
361 } catch (ConstructionException e) {
364 ci.println(this.addFlow(node, getSampleFlowV6(node)));
367 public void _removeflowv6(CommandInterpreter ci)
368 throws UnknownHostException {
370 String nodeId = ci.nextArgument();
371 if (nodeId == null) {
372 ci.print("Node id not specified");
376 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
377 } catch (NumberFormatException e) {
379 } catch (ConstructionException e) {
382 ci.println(this.removeFlow(node, getSampleFlowV6(node)));
385 private Flow getSampleFlow(Node node) throws UnknownHostException {
386 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
388 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
390 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
391 (byte) 0x9a, (byte) 0xbc };
392 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
393 (byte) 0x5e, (byte) 0x6f };
394 InetAddress srcIP = InetAddress.getByName("172.28.30.50");
395 InetAddress dstIP = InetAddress.getByName("171.71.9.52");
396 InetAddress newIP = InetAddress.getByName("200.200.100.1");
397 InetAddress ipMask = InetAddress.getByName("255.255.255.0");
398 InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
399 short ethertype = EtherTypes.IPv4.shortValue();
400 short vlan = (short) 27;
403 byte proto = IPProtocols.TCP.byteValue();
404 short src = (short) 55000;
408 * Create a SAL Flow aFlow
410 Match match = new Match();
411 match.setField(MatchType.IN_PORT, port);
412 match.setField(MatchType.DL_SRC, srcMac);
413 match.setField(MatchType.DL_DST, dstMac);
414 match.setField(MatchType.DL_TYPE, ethertype);
415 match.setField(MatchType.DL_VLAN, vlan);
416 match.setField(MatchType.DL_VLAN_PR, vlanPr);
417 match.setField(MatchType.NW_SRC, srcIP, ipMask);
418 match.setField(MatchType.NW_DST, dstIP, ipMask2);
419 match.setField(MatchType.NW_TOS, tos);
420 match.setField(MatchType.NW_PROTO, proto);
421 match.setField(MatchType.TP_SRC, src);
422 match.setField(MatchType.TP_DST, dst);
424 List<Action> actions = new ArrayList<Action>();
425 actions.add(new SetNwDst(newIP));
426 actions.add(new Output(oport));
427 actions.add(new PopVlan());
428 actions.add(new Flood());
429 actions.add(new Controller());
431 Flow flow = new Flow(match, actions);
432 flow.setPriority((short) 100);
433 flow.setHardTimeout((short) 360);
438 private Flow getSampleFlowV6(Node node) throws UnknownHostException {
439 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
441 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
443 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
444 (byte) 0x9a, (byte) 0xbc };
445 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
446 (byte) 0x5e, (byte) 0x6f };
447 InetAddress srcIP = InetAddress
448 .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
449 InetAddress dstIP = InetAddress
450 .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
451 InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
452 // V6Match implementation assumes no mask is
454 InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
455 short ethertype = EtherTypes.IPv6.shortValue();
456 short vlan = (short) 27;
457 byte vlanPr = (byte) 3;
459 byte proto = IPProtocols.UDP.byteValue();
460 short src = (short) 5500;
464 * Create a SAL Flow aFlow
466 Match match = new Match();
467 match.setField(MatchType.IN_PORT, port);
468 match.setField(MatchType.DL_SRC, srcMac);
469 match.setField(MatchType.DL_DST, dstMac);
470 match.setField(MatchType.DL_TYPE, ethertype);
471 match.setField(MatchType.DL_VLAN, vlan);
472 match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
474 match.setField(MatchType.NW_SRC, srcIP, ipMask);
475 match.setField(MatchType.NW_DST, dstIP, ipMask2);
476 match.setField(MatchType.NW_TOS, tos);
477 match.setField(MatchType.NW_PROTO, proto);
478 match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
480 // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
483 List<Action> actions = new ArrayList<Action>();
484 actions.add(new Output(oport));
485 actions.add(new PopVlan());
486 actions.add(new Flood());
488 Flow flow = new Flow(match, actions);
489 flow.setPriority((short) 300);
490 flow.setHardTimeout((short) 240);
496 * This Request ID generator starts with 1. Each aysnc message is
497 * associated with an unique Request ID (!= 0).
501 private long getNextRid() {
502 return seq.getAndIncrement();
506 public Status syncSendBarrierMessage(Node node) {
507 if (this.pluginFlowProgrammer != null) {
508 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
509 return this.pluginFlowProgrammer.get(node.getType())
510 .syncSendBarrierMessage(node);
513 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
517 public Status asyncSendBarrierMessage(Node node) {
518 if (this.pluginFlowProgrammer != null) {
519 if (this.pluginFlowProgrammer.get(node.getType()) != null) {
520 return this.pluginFlowProgrammer.get(node.getType())
521 .asyncSendBarrierMessage(node);
524 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");