2 * Copyright (c) 2013-2014 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, ProtocolService<IPluginInFlowProgrammerService>> pluginFlowProgrammer;
61 private Set<IFlowProgrammerListener> listener;
62 private AtomicLong seq;
64 public FlowProgrammerService() {
65 pluginFlowProgrammer = new ConcurrentHashMap<String, ProtocolService<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<String, Object> props, IPluginInFlowProgrammerService s) {
119 ProtocolService.set(this.pluginFlowProgrammer, props, s, logger);
122 public void unsetService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
123 ProtocolService.unset(this.pluginFlowProgrammer, props, s, logger);
126 public void setListener(IFlowProgrammerListener s) {
127 this.listener.add(s);
130 public void unsetListener(IFlowProgrammerListener s) {
131 this.listener.remove(s);
135 public Status addFlow(Node node, Flow flow) {
136 if (pluginFlowProgrammer != null) {
137 ProtocolService<IPluginInFlowProgrammerService> service =
138 this.pluginFlowProgrammer.get(node.getType());
139 if (service != null) {
140 return service.getService().addFlow(node, flow);
143 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
147 public Status removeFlow(Node node, Flow flow) {
148 if (pluginFlowProgrammer != null) {
149 ProtocolService<IPluginInFlowProgrammerService> service =
150 this.pluginFlowProgrammer.get(node.getType());
151 if (service != null) {
152 return service.getService().removeFlow(node, flow);
155 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
159 public Status removeAllFlows(Node node) {
160 if (pluginFlowProgrammer != null) {
161 ProtocolService<IPluginInFlowProgrammerService> service =
162 this.pluginFlowProgrammer.get(node.getType());
163 if (service != null) {
164 return service.getService().removeAllFlows(node);
167 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
171 public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
172 if (pluginFlowProgrammer != null) {
173 ProtocolService<IPluginInFlowProgrammerService> service =
174 this.pluginFlowProgrammer.get(node.getType());
175 if (service != null) {
176 return service.getService().modifyFlow(node, oldFlow, newFlow);
179 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
183 public Status addFlowAsync(Node node, Flow flow) {
184 if (pluginFlowProgrammer != null) {
185 ProtocolService<IPluginInFlowProgrammerService> service =
186 this.pluginFlowProgrammer.get(node.getType());
187 if (service != null) {
188 return service.getService().addFlowAsync(node, flow, getNextRid());
191 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
195 public Status removeFlowAsync(Node node, Flow flow) {
196 if (pluginFlowProgrammer != null) {
197 ProtocolService<IPluginInFlowProgrammerService> service =
198 this.pluginFlowProgrammer.get(node.getType());
199 if (service != null) {
200 return service.getService().removeFlowAsync(node, flow, getNextRid());
203 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
207 public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
208 if (pluginFlowProgrammer != null) {
209 ProtocolService<IPluginInFlowProgrammerService> service =
210 this.pluginFlowProgrammer.get(node.getType());
211 if (service != null) {
212 return service.getService().modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
215 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
219 public void flowRemoved(Node node, Flow flow) {
220 for (IFlowProgrammerListener l : listener) {
221 l.flowRemoved(node, flow);
226 public void flowErrorReported(Node node, long rid, Object err) {
227 logger.error("Got error {} for message rid {} from node {}",
228 new Object[] { err, rid, node });
230 for (IFlowProgrammerListener l : listener) {
231 l.flowErrorReported(node, rid, err);
235 // ---------------- OSGI TEST CODE ------------------------------//
237 private void registerWithOSGIConsole() {
238 BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
240 bundleContext.registerService(CommandProvider.class.getName(), this,
245 public String getHelp() {
246 StringBuffer help = new StringBuffer();
247 help.append("---SAL Flow Programmer testing commands---\n");
248 help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
249 help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
250 return help.toString();
253 public void _addflow(CommandInterpreter ci) throws UnknownHostException {
255 String nodeId = ci.nextArgument();
256 if (nodeId == null) {
257 ci.print("Node id not specified");
261 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
262 } catch (NumberFormatException e) {
264 } catch (ConstructionException e) {
267 ci.println(this.addFlow(node, getSampleFlow(node)));
270 public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
272 String nodeId = ci.nextArgument();
273 if (nodeId == null) {
274 ci.print("Node id not specified");
278 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
279 } catch (NumberFormatException e) {
281 } catch (ConstructionException e) {
284 Flow flowA = getSampleFlow(node);
285 Flow flowB = getSampleFlow(node);
286 Match matchB = flowB.getMatch();
287 matchB.setField(MatchType.NW_DST,
288 InetAddress.getByName("190.190.190.190"));
289 flowB.setMatch(matchB);
290 ci.println(this.modifyFlow(node, flowA, flowB));
293 public void _removeflow(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.removeFlow(node, getSampleFlow(node)));
310 public void _addflowv6(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 ci.println(this.addFlow(node, getSampleFlowV6(node)));
327 public void _removeflowv6(CommandInterpreter ci)
328 throws UnknownHostException {
330 String nodeId = ci.nextArgument();
331 if (nodeId == null) {
332 ci.print("Node id not specified");
336 node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
337 } catch (NumberFormatException e) {
339 } catch (ConstructionException e) {
342 ci.println(this.removeFlow(node, getSampleFlowV6(node)));
345 private Flow getSampleFlow(Node node) throws UnknownHostException {
346 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
348 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
350 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
351 (byte) 0x9a, (byte) 0xbc };
352 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
353 (byte) 0x5e, (byte) 0x6f };
354 InetAddress srcIP = InetAddress.getByName("172.28.30.50");
355 InetAddress dstIP = InetAddress.getByName("171.71.9.52");
356 InetAddress newIP = InetAddress.getByName("200.200.100.1");
357 InetAddress ipMask = InetAddress.getByName("255.255.255.0");
358 InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
359 short ethertype = EtherTypes.IPv4.shortValue();
360 short vlan = (short) 27;
363 byte proto = IPProtocols.TCP.byteValue();
364 short src = (short) 55000;
368 * Create a SAL Flow aFlow
370 Match match = new Match();
371 match.setField(MatchType.IN_PORT, port);
372 match.setField(MatchType.DL_SRC, srcMac);
373 match.setField(MatchType.DL_DST, dstMac);
374 match.setField(MatchType.DL_TYPE, ethertype);
375 match.setField(MatchType.DL_VLAN, vlan);
376 match.setField(MatchType.DL_VLAN_PR, vlanPr);
377 match.setField(MatchType.NW_SRC, srcIP, ipMask);
378 match.setField(MatchType.NW_DST, dstIP, ipMask2);
379 match.setField(MatchType.NW_TOS, tos);
380 match.setField(MatchType.NW_PROTO, proto);
381 match.setField(MatchType.TP_SRC, src);
382 match.setField(MatchType.TP_DST, dst);
384 List<Action> actions = new ArrayList<Action>();
385 actions.add(new SetNwDst(newIP));
386 actions.add(new Output(oport));
387 actions.add(new PopVlan());
388 actions.add(new Flood());
389 actions.add(new Controller());
391 Flow flow = new Flow(match, actions);
392 flow.setPriority((short) 100);
393 flow.setHardTimeout((short) 360);
399 private Flow getSampleFlowV6(Node node) throws UnknownHostException {
400 NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
402 NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
404 byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
405 (byte) 0x9a, (byte) 0xbc };
406 byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
407 (byte) 0x5e, (byte) 0x6f };
408 InetAddress srcIP = InetAddress
409 .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
410 InetAddress dstIP = InetAddress
411 .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
412 InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
413 // V6Match implementation assumes no mask is
415 InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
416 short ethertype = EtherTypes.IPv6.shortValue();
417 short vlan = (short) 27;
418 byte vlanPr = (byte) 3;
420 byte proto = IPProtocols.UDP.byteValue();
421 short src = (short) 5500;
425 * Create a SAL Flow aFlow
427 Match match = new Match();
428 match.setField(MatchType.IN_PORT, port);
429 match.setField(MatchType.DL_SRC, srcMac);
430 match.setField(MatchType.DL_DST, dstMac);
431 match.setField(MatchType.DL_TYPE, ethertype);
432 match.setField(MatchType.DL_VLAN, vlan);
433 match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
435 match.setField(MatchType.NW_SRC, srcIP, ipMask);
436 match.setField(MatchType.NW_DST, dstIP, ipMask2);
437 match.setField(MatchType.NW_TOS, tos);
438 match.setField(MatchType.NW_PROTO, proto);
439 match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
441 // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
444 List<Action> actions = new ArrayList<Action>();
445 actions.add(new Output(oport));
446 actions.add(new PopVlan());
447 actions.add(new Flood());
449 Flow flow = new Flow(match, actions);
450 flow.setPriority((short) 300);
451 flow.setHardTimeout((short) 240);
458 * This Request ID generator starts with 1. Each aysnc message is
459 * associated with an unique Request ID (!= 0).
463 private long getNextRid() {
464 return seq.getAndIncrement();
468 public Status syncSendBarrierMessage(Node node) {
469 if (this.pluginFlowProgrammer != null) {
470 ProtocolService<IPluginInFlowProgrammerService> service =
471 this.pluginFlowProgrammer.get(node.getType());
472 if (service != null) {
473 return service.getService().syncSendBarrierMessage(node);
476 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
480 public Status asyncSendBarrierMessage(Node node) {
481 if (this.pluginFlowProgrammer != null) {
482 ProtocolService<IPluginInFlowProgrammerService> service =
483 this.pluginFlowProgrammer.get(node.getType());
484 if (service != null) {
485 return service.getService().asyncSendBarrierMessage(node);
488 return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");