carlosmscabral | f40ecd1 | 2013-02-01 18:15:58 -0200 | [diff] [blame^] | 1 | From 5c9610ffb88c89b0f36359ad3c7547831482a3ff Mon Sep 17 00:00:00 2001 |
| 2 | From: Bob Lantz <rlantz@cs.stanford.edu> |
| 3 | Date: Fri, 3 Feb 2012 14:48:58 -0800 |
| 4 | Subject: [PATCH] OpenFlow tutorial port nox-destiny. |
| 5 | |
| 6 | --- |
| 7 | src/nox/coreapps/examples/Makefile.am | 2 +- |
| 8 | src/nox/coreapps/examples/tutorial/Makefile.am | 25 ++++ |
| 9 | src/nox/coreapps/examples/tutorial/meta.json | 12 ++ |
| 10 | src/nox/coreapps/examples/tutorial/pytutorial.py | 67 +++++++++++ |
| 11 | src/nox/coreapps/examples/tutorial/tutorial.cc | 134 ++++++++++++++++++++++ |
| 12 | 5 files changed, 239 insertions(+), 1 deletions(-) |
| 13 | create mode 100644 src/nox/coreapps/examples/tutorial/Makefile.am |
| 14 | create mode 100644 src/nox/coreapps/examples/tutorial/__init__.py |
| 15 | create mode 100644 src/nox/coreapps/examples/tutorial/meta.json |
| 16 | create mode 100644 src/nox/coreapps/examples/tutorial/pytutorial.py |
| 17 | create mode 100644 src/nox/coreapps/examples/tutorial/tutorial.cc |
| 18 | |
| 19 | diff --git a/src/nox/coreapps/examples/Makefile.am b/src/nox/coreapps/examples/Makefile.am |
| 20 | index 126f32e..1a0458c 100644 |
| 21 | --- a/src/nox/coreapps/examples/Makefile.am |
| 22 | +++ b/src/nox/coreapps/examples/Makefile.am |
| 23 | @@ -1,6 +1,6 @@ |
| 24 | include ../../../Make.vars |
| 25 | |
| 26 | -SUBDIRS = t |
| 27 | +SUBDIRS = tutorial t |
| 28 | |
| 29 | EXTRA_DIST =\ |
| 30 | meta.json\ |
| 31 | diff --git a/src/nox/coreapps/examples/tutorial/Makefile.am b/src/nox/coreapps/examples/tutorial/Makefile.am |
| 32 | new file mode 100644 |
| 33 | index 0000000..51cf921 |
| 34 | --- /dev/null |
| 35 | +++ b/src/nox/coreapps/examples/tutorial/Makefile.am |
| 36 | @@ -0,0 +1,25 @@ |
| 37 | +include ../../../../Make.vars |
| 38 | + |
| 39 | +EXTRA_DIST =\ |
| 40 | + meta.xml \ |
| 41 | + __init__.py \ |
| 42 | + pytutorial.py |
| 43 | + |
| 44 | +if PY_ENABLED |
| 45 | +AM_CPPFLAGS += $(PYTHON_CPPFLAGS) |
| 46 | +endif # PY_ENABLED |
| 47 | + |
| 48 | +pkglib_LTLIBRARIES = \ |
| 49 | + tutorial.la |
| 50 | + |
| 51 | +tutorial_la_CPPFLAGS = $(AM_CPPFLAGS) -I $(top_srcdir)/src/nox -I $(top_srcdir)/src/nox/coreapps/ |
| 52 | +tutorial_la_SOURCES = tutorial.cc |
| 53 | +tutorial_la_LDFLAGS = -module -export-dynamic |
| 54 | + |
| 55 | +NOX_RUNTIMEFILES = meta.json \ |
| 56 | + __init__.py \ |
| 57 | + pytutorial.py |
| 58 | + |
| 59 | +all-local: nox-all-local |
| 60 | +clean-local: nox-clean-local |
| 61 | +install-exec-hook: nox-install-local |
| 62 | diff --git a/src/nox/coreapps/examples/tutorial/__init__.py b/src/nox/coreapps/examples/tutorial/__init__.py |
| 63 | new file mode 100644 |
| 64 | index 0000000..e69de29 |
| 65 | diff --git a/src/nox/coreapps/examples/tutorial/meta.json b/src/nox/coreapps/examples/tutorial/meta.json |
| 66 | new file mode 100644 |
| 67 | index 0000000..7a9f227 |
| 68 | --- /dev/null |
| 69 | +++ b/src/nox/coreapps/examples/tutorial/meta.json |
| 70 | @@ -0,0 +1,12 @@ |
| 71 | +{ |
| 72 | + "components": [ |
| 73 | + { |
| 74 | + "name": "tutorial", |
| 75 | + "library": "tutorial" |
| 76 | + }, |
| 77 | + { |
| 78 | + "name": "pytutorial", |
| 79 | + "python": "nox.coreapps.examples.tutorial.pytutorial" |
| 80 | + } |
| 81 | + ] |
| 82 | +} |
| 83 | diff --git a/src/nox/coreapps/examples/tutorial/pytutorial.py b/src/nox/coreapps/examples/tutorial/pytutorial.py |
| 84 | new file mode 100644 |
| 85 | index 0000000..1e21c0b |
| 86 | --- /dev/null |
| 87 | +++ b/src/nox/coreapps/examples/tutorial/pytutorial.py |
| 88 | @@ -0,0 +1,67 @@ |
| 89 | +# Tutorial Controller |
| 90 | +# Starts as a hub, and your job is to turn this into a learning switch. |
| 91 | + |
| 92 | +import logging |
| 93 | + |
| 94 | +from nox.lib.core import * |
| 95 | +import nox.lib.openflow as openflow |
| 96 | +from nox.lib.packet.ethernet import ethernet |
| 97 | +from nox.lib.packet.packet_utils import mac_to_str, mac_to_int |
| 98 | + |
| 99 | +log = logging.getLogger('nox.coreapps.tutorial.pytutorial') |
| 100 | + |
| 101 | + |
| 102 | +class pytutorial(Component): |
| 103 | + |
| 104 | + def __init__(self, ctxt): |
| 105 | + Component.__init__(self, ctxt) |
| 106 | + # Use this table to store MAC addresses in the format of your choice; |
| 107 | + # Functions already imported, including mac_to_str, and mac_to_int, |
| 108 | + # should prove useful for converting the byte array provided by NOX |
| 109 | + # for packet MAC destination fields. |
| 110 | + # This table is initialized to empty when your module starts up. |
| 111 | + self.mac_to_port = {} # key: MAC addr; value: port |
| 112 | + |
| 113 | + def learn_and_forward(self, dpid, inport, packet, buf, bufid): |
| 114 | + """Learn MAC src port mapping, then flood or send unicast.""" |
| 115 | + |
| 116 | + # Initial hub behavior: flood packet out everything but input port. |
| 117 | + # Comment out the line below when starting the exercise. |
| 118 | + self.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport) |
| 119 | + |
| 120 | + # Starter psuedocode for learning switch exercise below: you'll need to |
| 121 | + # replace each pseudocode line with more specific Python code. |
| 122 | + |
| 123 | + # Learn the port for the source MAC |
| 124 | + #self.mac_to_port = <fill in> |
| 125 | + #if (destination MAC of the packet is known): |
| 126 | + # Send unicast packet to known output port |
| 127 | + #self.send_openflow( <fill in params> ) |
| 128 | + # Later, only after learning controller works: |
| 129 | + # push down flow entry and remove the send_openflow command above. |
| 130 | + #self.install_datapath_flow( <fill in params> ) |
| 131 | + #else: |
| 132 | + #flood packet out everything but the input port |
| 133 | + #self.send_openflow(dpid, bufid, buf, openflow.OFPP_FLOOD, inport) |
| 134 | + |
| 135 | + def packet_in_callback(self, dpid, inport, reason, len, bufid, packet): |
| 136 | + """Packet-in handler""" |
| 137 | + if not packet.parsed: |
| 138 | + log.debug('Ignoring incomplete packet') |
| 139 | + else: |
| 140 | + self.learn_and_forward(dpid, inport, packet, packet.arr, bufid) |
| 141 | + |
| 142 | + return CONTINUE |
| 143 | + |
| 144 | + def install(self): |
| 145 | + self.register_for_packet_in(self.packet_in_callback) |
| 146 | + |
| 147 | + def getInterface(self): |
| 148 | + return str(pytutorial) |
| 149 | + |
| 150 | +def getFactory(): |
| 151 | + class Factory: |
| 152 | + def instance(self, ctxt): |
| 153 | + return pytutorial(ctxt) |
| 154 | + |
| 155 | + return Factory() |
| 156 | diff --git a/src/nox/coreapps/examples/tutorial/tutorial.cc b/src/nox/coreapps/examples/tutorial/tutorial.cc |
| 157 | new file mode 100644 |
| 158 | index 0000000..e7240cc |
| 159 | --- /dev/null |
| 160 | +++ b/src/nox/coreapps/examples/tutorial/tutorial.cc |
| 161 | @@ -0,0 +1,134 @@ |
| 162 | +#include "component.hh" |
| 163 | +#include "config.h" |
| 164 | +#include "packet-in.hh" |
| 165 | +#include "flow.hh" |
| 166 | +#include "assert.hh" |
| 167 | +#include "netinet++/ethernetaddr.hh" |
| 168 | +#include "netinet++/ethernet.hh" |
| 169 | +#include <boost/shared_array.hpp> |
| 170 | +#include <boost/bind.hpp> |
| 171 | +#ifdef LOG4CXX_ENABLED |
| 172 | +#include <boost/format.hpp> |
| 173 | +#include "log4cxx/logger.h" |
| 174 | +#else |
| 175 | +#include "vlog.hh" |
| 176 | +#endif |
| 177 | + |
| 178 | +using namespace std; |
| 179 | +using namespace vigil; |
| 180 | +using namespace vigil::container; |
| 181 | + |
| 182 | +namespace |
| 183 | +{ |
| 184 | + static Vlog_module lg("tutorial"); |
| 185 | + |
| 186 | + /** Learning switch. |
| 187 | + */ |
| 188 | + class tutorial |
| 189 | + : public Component |
| 190 | + { |
| 191 | + public: |
| 192 | + /** Constructor. |
| 193 | + */ |
| 194 | + tutorial(const Context* c, const json_object* node) |
| 195 | + : Component(c) |
| 196 | + { } |
| 197 | + |
| 198 | + /** Configuration. |
| 199 | + * Add handler for packet-in event. |
| 200 | + */ |
| 201 | + void configure(const Configuration*) |
| 202 | + { |
| 203 | + register_handler<Packet_in_event> |
| 204 | + (boost::bind(&tutorial::handle, this, _1)); |
| 205 | + } |
| 206 | + |
| 207 | + /** Just simply install. |
| 208 | + */ |
| 209 | + void install() |
| 210 | + { |
| 211 | + lg.dbg(" Install called "); |
| 212 | + } |
| 213 | + |
| 214 | + /** Function to setup flow. |
| 215 | + */ |
| 216 | + void setup_flow(Flow& flow, datapathid datapath_id , |
| 217 | + uint32_t buffer_id, uint16_t out_port) |
| 218 | + { |
| 219 | + ofp_flow_mod* ofm; |
| 220 | + size_t size = sizeof *ofm + sizeof(ofp_action_output); |
| 221 | + boost::shared_array<char> raw_of(new char[size]); |
| 222 | + ofm = (ofp_flow_mod*) raw_of.get(); |
| 223 | + |
| 224 | + ofm->header.version = OFP_VERSION; |
| 225 | + ofm->header.type = OFPT_FLOW_MOD; |
| 226 | + ofm->header.length = htons(size); |
| 227 | + ofm->match.wildcards = htonl(0); |
| 228 | + ofm->match.in_port = htons(flow.in_port); |
| 229 | + ofm->match.dl_vlan = flow.dl_vlan; |
| 230 | + memcpy(ofm->match.dl_src, flow.dl_src.octet, sizeof ofm->match.dl_src); |
| 231 | + memcpy(ofm->match.dl_dst, flow.dl_dst.octet, sizeof ofm->match.dl_dst); |
| 232 | + ofm->match.dl_type = flow.dl_type; |
| 233 | + ofm->match.nw_src = flow.nw_src; |
| 234 | + ofm->match.nw_dst = flow.nw_dst; |
| 235 | + ofm->match.nw_proto = flow.nw_proto; |
| 236 | + ofm->match.tp_src = flow.tp_src; |
| 237 | + ofm->match.tp_dst = flow.tp_dst; |
| 238 | + ofm->command = htons(OFPFC_ADD); |
| 239 | + ofm->buffer_id = htonl(buffer_id); |
| 240 | + ofm->idle_timeout = htons(5); |
| 241 | + ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); |
| 242 | + ofm->priority = htons(OFP_DEFAULT_PRIORITY); |
| 243 | + ofp_action_output& action = *((ofp_action_output*)ofm->actions); |
| 244 | + memset(&action, 0, sizeof(ofp_action_output)); |
| 245 | + action.type = htons(OFPAT_OUTPUT); |
| 246 | + action.len = htons(sizeof(ofp_action_output)); |
| 247 | + action.max_len = htons(0); |
| 248 | + action.port = htons(out_port); |
| 249 | + send_openflow_command(datapath_id, &ofm->header, true); |
| 250 | + } |
| 251 | + |
| 252 | + /** Function to handle packets. |
| 253 | + * @param datapath_id datapath id of switch |
| 254 | + * @param in_port port packet is received |
| 255 | + * @param buffer_id buffer id of packet |
| 256 | + * @param source source mac address in host order |
| 257 | + * @param destination destination mac address in host order |
| 258 | + */ |
| 259 | + void handle_packet(datapathid datapath_id, uint16_t in_port, uint32_t buffer_id, |
| 260 | + uint64_t source, uint64_t destination) |
| 261 | + { |
| 262 | + send_openflow_packet(datapath_id, buffer_id, OFPP_FLOOD, |
| 263 | + in_port, true); |
| 264 | + } |
| 265 | + |
| 266 | + /** Packet-on handler. |
| 267 | + */ |
| 268 | + Disposition handle(const Event& e) |
| 269 | + { |
| 270 | + const Packet_in_event& pi = assert_cast<const Packet_in_event&>(e); |
| 271 | + uint32_t buffer_id = pi.buffer_id; |
| 272 | + Flow flow(pi.in_port, *pi.get_buffer()); |
| 273 | + |
| 274 | + // drop LLDP packets |
| 275 | + if (flow.dl_type == ethernet::LLDP) |
| 276 | + return CONTINUE; |
| 277 | + |
| 278 | + // pass handle of unicast packet, else flood |
| 279 | + if (!flow.dl_src.is_multicast()) |
| 280 | + handle_packet(pi.datapath_id, pi.in_port, buffer_id, |
| 281 | + flow.dl_src.hb_long(), flow.dl_dst.hb_long()); |
| 282 | + else |
| 283 | + send_openflow_packet(pi.datapath_id, buffer_id, OFPP_FLOOD, |
| 284 | + pi.in_port, true); |
| 285 | + |
| 286 | + return CONTINUE; |
| 287 | + } |
| 288 | + |
| 289 | + private: |
| 290 | +}; |
| 291 | + |
| 292 | +REGISTER_COMPONENT(container::Simple_component_factory<tutorial>, |
| 293 | + tutorial); |
| 294 | + |
| 295 | +} // unnamed namespace |
| 296 | -- |
| 297 | 1.7.5.4 |
| 298 | |