This section describes NOX’s core API. This is the basic interface applications use to gain access to network events, control network components, and interface with each other.
The following documentation refers to the public methods in src/nox/component.hh.
The primary mechanism for gaining access to network events is to register a handler using one of the following methods:
void register_handler(const Event_name&, const Event_handler&) const;
inline void register_handler<Event>(const Event_handler& h) const;
The following example registers a callback for all incoming packets to NOX:
// NOTE: example only, not a valid NOX Component
class Example{
public:
Disposition print(const Event& e)
{
using namespace std;
const Packet_in_event& pi = assert_cast<const Packet_in_event&>(e);
cout << " Received packet in on port " << pi.in_port << endl;
return CONTINUE; // keep processing the packet
}
// at application load time, register handler
void install()
{
register_handler<Packet_in_event>(
Packet_in_event::static_get_name(),
boost::bind(&Example::print, this, _1));
}
}; // class Example
An event handler is a boost::function and must return a Disposition and accept a NOX Event (src/include/event.hh). Return values can be either of the following:
NOX core event types are defined in files in src/include/. These include:
All other events are generated and thrown by applications.
In many cases an application is only interested in a particular type of traffic from the network (for example, only DNS and DHCP). For this, NOX provides a convenience function which allows the application to specify the header values for the packets it is interested in.:
Rule_id register_handler_on_match(uint32_t priority,
const Packet_expr &,
Pexpr_action) const;
Packet_expr is defined in src/include/expr.hh. The handler (Pexpr_action) differs from the standard handler in that it does not return a Disposition value. When NOX receives a packet, only matching handler(s) with the lowest priority value will be invoked.
For example, registering only for TCP packets looks like:
// only call on TCP packets
Packet_expr expr;
uint32_t val = ethernet::IP;
expr.set_field(Packet_expr::DL_TYPE, &val);
val = ip_::proto::TCP;
expr.set_field(Packet_expr::NW_PROTO, &val);
register_handler_on_match(100,
expr,
boost::bind(&Example::tcp_packet_handler, this, _1));
Any application can create and post events for other applications to handle using the following method:
void post(Event*) const;
Warning
Events passed to post() are assumed to be dynamically allocated and are freed by NOX once fully dispatched.
Posting an event is simple. For example:
post(new Flow_in_event(flow, *src, *dst, src_dl_authed,
src_nw_authed, dst_dl_authed, dst_nw_authed, pi));
In NOX, all execution is event-driven (this isn’t entirely true, but unless you want to muck around with native threads, it’s a reasonable assumption). Applications can ask NOX to call a handler after some amount of time has lapsed, forming the basis for timer creation. This functionality is also done using the post method:
Timer post(const Timer_Callback&, const timeval& duration) const;
For example, registering a method to be called every second might look like:
void timer(){
using namespace std;
cout << "One second has passed " << endl;
timevale tv={1,0}
post(boost::bind(&Example::timer, this), tv);
}
timevale tv={1,0}
post(boost::bind(&Example::timer, this), tv);
Applications manage forwarding on the network by adding and deleting flow entries in switches. NOX exposes the full functionality supported by the OpenFlow management protocol. The general function doing this is:
int send_openflow_command(const datapathid&, const ofp_header*,
bool block) const;
OpenFlow message formats and definitions are contained in src/include/openflow.hh.
Warning
Unfortunately this method is quite low level at the moment and requires callers to construct their own OpenFlow messages (yuck). We will provide convenience functions for the common OpenFlow operations in a future release. Until then, you can use src/nox/apps/switch/switch.cc as a guide to setting up a flow.
Applications can send packets on the network using one the following methods:
// Instruct a switch to send a packet it has buffered
int send_openflow_packet(const datapathid&, uint32_t buffer_id,
uint16_t out_port, uint16_t in_port,
bool block) const;
// Send a packet in a Buffer object out on the network
int send_openflow_packet(const datapathid&, const Buffer&,
uint16_t out_port, uint16_t in_port,
bool block) const;
The first method accepts a buffer_id and assumes that the switch is already buffering the packet. Unless otherwise instructed, switches buffer incoming packets and only send the first 128 bytes to the controller. The buffer_id is then passed to the application in a Packet_in_event. Use the first method to send this packet out again (see src/nox/apps/hub/hub.cc for an example).
The second method allows an application to construct and send an arbitrary packet out on the network.
Warning
This section is incomplete. Refer to files in src/nox/apps/examples/ for guidance.
NOX’s Python API is defined in “core.py” in src/nox/lib/. It exposes the same functionality as the C++ API, but attempts to remove some of the complexity (e.g. more convenient methods to modify switch flow tables). Many of the C++ definitions needed to interact with NOX are exposed to Python in SWIG *.i files also located in src/nox/lib/. These include netinet and OpenFlow definitions. Events and components meanwhile are exposed in the *.i files in src/nox/apps/pyrt/.
A packet handling library has also been defined in src/nox/lib/packet/ for convenient packet construction and parsing.