src/lib/openflow_switch_interface.c
/*
* Author: Yasunobu Chiba
*
* Copyright (C) 2008-2013 NEC Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <assert.h>
#include <inttypes.h>
#include <arpa/inet.h>
#include <syslog.h>
#include "hash_table.h"
#include "log.h"
#include "messenger.h"
#include "openflow_message.h"
#include "openflow_service_interface.h"
#include "openflow_switch_interface.h"
#include "safe_timer.h"
#include "secure_channel.h"
#include "wrapper.h"
#ifdef UNIT_TESTING
#define static
#ifdef send_message_to_secure_channel
#undef send_message_to_secure_channel
#endif
#define send_message_to_secure_channel mock_send_message_to_secure_channel
bool mock_send_message_to_secure_channel( buffer *message );
#ifdef send_message
#undef send_message
#endif
#define send_message mock_send_message
bool mock_send_message( const char *service_name, const uint16_t tag, const void *data, size_t len );
#ifdef init_secure_channel
#undef init_secure_channel
#endif
#define init_secure_channel mock_init_secure_channel
bool mock_init_secure_channel( uint32_t ip, uint16_t port,
connected_handler connected_callback, disconnected_handler disconnected_callback );
#ifdef add_periodic_event_callback
#undef add_periodic_event_callback
#endif
#define add_periodic_event_callback mock_add_periodic_event_callback
bool mock_add_periodic_event_callback( const time_t seconds, timer_callback callback, void *user_data );
#ifdef finalize_secure_channel
#undef finalize_secure_channel
#endif
#define finalize_secure_channel mock_finalize_secure_channel
bool mock_finalize_secure_channel();
#ifdef delete_timer_event
#undef delete_timer_event
#endif
#define delete_timer_event mock_delete_timer_event
bool mock_delete_timer_event( timer_callback callback, void *user_data );
#ifdef add_message_received_callback
#undef add_message_received_callback
#endif
#define add_message_received_callback mock_add_message_received_callback
bool mock_add_message_received_callback( const char *service_name,
void ( *callback )( uint16_t tag, void *data, size_t len ) );
#ifdef getpid
#undef getpid
#endif
#define getpid mock_getpid
pid_t mock_getpid( void );
#ifdef debug
#undef debug
#endif
#define debug mock_debug
extern void mock_debug( const char *format, ... );
#ifdef error
#undef error
#endif
#define error mock_error
extern void mock_error( const char *format, ... );
#endif // UNIT_TESTING
typedef struct {
uint64_t datapath_id;
struct {
uint32_t ip;
uint16_t port;
} controller;
} openflow_switch_config;
typedef bool ( *message_send_handler )( buffer *message, void *user_data );
typedef struct {
uint32_t transaction_id;
buffer *message;
message_send_handler send_callback;
void *user_data;
time_t created_at;
} openflow_context;
static bool openflow_switch_interface_initialized = false;
static openflow_switch_event_handlers event_handlers;
static openflow_switch_config config;
static const int CONTEXT_LIFETIME = 5;
static hash_table *contexts = NULL;
static bool
compare_context( const void *x, const void *y ) {
const openflow_context *cx = x;
const openflow_context *cy = y;
return ( cx->transaction_id == cy->transaction_id ) ? true : false;
}
static unsigned int
hash_context( const void *key ) {
return ( unsigned int ) *( ( const uint32_t * ) key );
}
static openflow_context *
lookup_context( uint32_t transaction_id ) {
assert( contexts != NULL );
return lookup_hash_entry( contexts, &transaction_id );
}
static bool
save_context( uint32_t transaction_id, buffer *message, message_send_handler callback, void *user_data ) {
assert( contexts != NULL );
openflow_context *context = lookup_context( transaction_id );
if ( context != NULL ) {
return false;
}
context = xmalloc( sizeof( openflow_context ) );
memset( context, 0, sizeof( openflow_context ) );
context->transaction_id = transaction_id;
context->message = duplicate_buffer( message );
context->send_callback = callback;
context->user_data = user_data;
context->created_at = time( NULL );
insert_hash_entry( contexts, &context->transaction_id, context );
return true;
}
static void
delete_context( uint32_t transaction_id ) {
assert( contexts != NULL );
openflow_context *context = delete_hash_entry( contexts, &transaction_id );
if ( context == NULL ) {
return;
}
if ( context->message != NULL ) {
free_buffer( context->message );
}
if ( context->user_data != NULL ) {
xfree( context->user_data );
}
xfree( context );
}
static void
age_contexts( void *user_data ) {
UNUSED( user_data );
time_t now = time( NULL );
hash_iterator iter;
init_hash_iterator( contexts, &iter );
hash_entry *e = NULL;
while ( ( e = iterate_hash_next( &iter ) ) != NULL ) {
openflow_context *context = e->value;
if ( ( context != NULL ) && ( ( context->created_at + CONTEXT_LIFETIME ) <= now ) ) {
delete_context( context->transaction_id );
}
}
}
static void
init_context() {
assert( contexts == NULL );
contexts = create_hash_with_size( compare_context, hash_context, 128 );
}
static void
finalize_context() {
assert( contexts != NULL );
hash_iterator iter;
init_hash_iterator( contexts, &iter );
hash_entry *e = NULL;
while ( ( e = iterate_hash_next( &iter ) ) != NULL ) {
openflow_context *context = e->value;
if ( context != NULL ) {
delete_context( context->transaction_id );
}
}
delete_hash( contexts );
contexts = NULL;
}
bool
openflow_switch_interface_is_initialized() {
return openflow_switch_interface_initialized;
}
bool
set_openflow_switch_event_handlers( const openflow_switch_event_handlers handlers ) {
assert( openflow_switch_interface_initialized );
memcpy( &event_handlers, &handlers, sizeof( event_handlers ) );
return true;
}
bool
set_controller_connected_handler( controller_connected_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a controller connected handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.controller_connected_callback = callback;
event_handlers.controller_connected_user_data = user_data;
return true;
}
bool
set_controller_disconnected_handler( controller_disconnected_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a controller disconnected handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.controller_disconnected_callback = callback;
event_handlers.controller_disconnected_user_data = user_data;
return true;
}
bool
set_hello_handler( hello_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a hello handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.hello_callback = callback;
event_handlers.hello_user_data = user_data;
return true;
}
bool
switch_set_error_handler( switch_error_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting an error handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.error_callback = callback;
event_handlers.error_user_data = user_data;
return true;
}
bool
switch_set_experimenter_error_handler( switch_experimenter_error_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting an experimenter error handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.experimenter_error_callback = callback;
event_handlers.experimenter_error_user_data = user_data;
return true;
}
bool
set_echo_request_handler( echo_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting an echo request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.echo_request_callback = callback;
event_handlers.echo_request_user_data = user_data;
return true;
}
bool
switch_set_echo_reply_handler( switch_echo_reply_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting an echo reply handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.echo_reply_callback = callback;
event_handlers.echo_reply_user_data = user_data;
return true;
}
bool
switch_set_experimenter_handler( switch_experimenter_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a experimenter handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.experimenter_callback = callback;
event_handlers.experimenter_user_data = user_data;
return true;
}
bool
set_features_request_handler( features_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a features request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.features_request_callback = callback;
event_handlers.features_request_user_data = user_data;
return true;
}
bool
set_get_config_request_handler( get_config_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a get config request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.get_config_request_callback = callback;
event_handlers.get_config_request_user_data = user_data;
return true;
}
bool
set_set_config_handler( set_config_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a set config handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.set_config_callback = callback;
event_handlers.set_config_user_data = user_data;
return true;
}
bool
set_packet_out_handler( packet_out_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a packet out handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.packet_out_callback = callback;
event_handlers.packet_out_user_data = user_data;
return true;
}
bool
set_flow_mod_handler( flow_mod_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a flow mod handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.flow_mod_callback = callback;
event_handlers.flow_mod_user_data = user_data;
return true;
}
bool
set_group_mod_handler( group_mod_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a group mod handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.group_mod_callback = callback;
event_handlers.group_mod_user_data = user_data;
return true;
}
bool
set_port_mod_handler( port_mod_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a port mod handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.port_mod_callback = callback;
event_handlers.port_mod_user_data = user_data;
return true;
}
bool
set_table_mod_handler( table_mod_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a table mod handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.table_mod_callback = callback;
event_handlers.table_mod_user_data = user_data;
return true;
}
bool
set_multipart_request_handler( multipart_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a multipart request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.multipart_request_callback = callback;
event_handlers.multipart_request_user_data = user_data;
return true;
}
bool
set_barrier_request_handler( barrier_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a barrier request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.barrier_request_callback = callback;
event_handlers.barrier_request_user_data = user_data;
return true;
}
bool
set_queue_get_config_request_handler( queue_get_config_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a queue get config request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.queue_get_config_request_callback = callback;
event_handlers.queue_get_config_request_user_data = user_data;
return true;
}
bool
set_role_request_handler( role_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a role request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.role_request_callback = callback;
event_handlers.role_request_user_data = user_data;
return true;
}
bool
set_get_async_request_handler( get_async_request_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a get async request handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.get_async_request_callback = callback;
event_handlers.get_async_request_user_data = user_data;
return true;
}
bool
set_set_async_handler( set_async_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a set async handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.set_async_callback = callback;
event_handlers.set_async_user_data = user_data;
return true;
}
bool
set_meter_mod_handler( meter_mod_handler callback, void *user_data ) {
assert( callback != NULL );
assert( openflow_switch_interface_initialized );
debug( "Setting a meter mod handler ( callback = %p, user_data = %p ).", callback, user_data );
event_handlers.meter_mod_callback = callback;
event_handlers.meter_mod_user_data = user_data;
return true;
}
static bool
empty( const buffer *data ) {
if ( ( data == NULL ) || ( ( data != NULL ) && ( data->length == 0 ) ) ) {
return true;
}
return false;
}
static void
handle_hello( buffer *data ) {
assert( empty( data ) == false );
struct ofp_hello *hello = data->data;
uint32_t transaction_id = ntohl( hello->header.xid );
uint8_t version = hello->header.version;
buffer *elements = NULL;
if ( ntohs( hello->header.length ) > sizeof( struct ofp_hello ) ) {
elements = duplicate_buffer( data );
remove_front_buffer( elements, offsetof( struct ofp_hello, elements ) );
struct ofp_hello_elem_header *element = elements->data;
size_t elements_length = ntohs( hello->header.length ) - offsetof( struct ofp_hello, elements );
while ( elements_length >= sizeof( struct ofp_hello_elem_header ) ) {
ntoh_hello_elem( element, element );
uint16_t element_length = ( uint16_t ) ( element->length + PADLEN_TO_64( element->length ) );
elements_length -= element_length;
element = ( struct ofp_hello_elem_header * ) ( ( char * ) element + element_length );
}
}
debug( "A hello message is received ( transaction_id = %#x, version = %#x ).", transaction_id, version );
if ( event_handlers.hello_callback == NULL ) {
debug( "Callback function for hello events is not set." );
if ( elements != NULL ) {
free_buffer( elements );
}
return;
}
debug( "Calling hello handler ( callback = %p, user_data = %p ).",
event_handlers.hello_callback, event_handlers.hello_user_data );
event_handlers.hello_callback( transaction_id,
version,
elements,
event_handlers.hello_user_data );
if ( elements != NULL ) {
free_buffer( elements );
}
}
static void
handle_experimenter_error( buffer *data );
static void
handle_error( buffer *data ) {
assert( empty( data ) == false );
struct ofp_error_msg *error_msg = ( struct ofp_error_msg * ) data->data;
uint32_t transaction_id = ntohl( error_msg->header.xid );
uint16_t type = ntohs( error_msg->type );
if ( type == OFPET_EXPERIMENTER ) {
handle_experimenter_error( data );
return;
}
uint16_t code = ntohs( error_msg->code );
buffer *body = duplicate_buffer( data );
remove_front_buffer( body, offsetof( struct ofp_error_msg, data ) );
debug( "An error message is received ( transaction_id = %#x, type = %#x, code = %#x, data length = %u ).",
transaction_id, type, code, body->length );
if ( event_handlers.error_callback == NULL ) {
debug( "Callback function for error events is not set." );
free_buffer( body );
return;
}
debug( "Calling error handler ( callback = %p, user_data = %p ).",
event_handlers.error_callback, event_handlers.error_user_data );
event_handlers.error_callback( transaction_id,
type,
code,
body,
event_handlers.error_user_data );
free_buffer( body );
}
static void
handle_experimenter_error( buffer *data ) {
assert( empty( data ) == false );
struct ofp_error_experimenter_msg *experimenter_error_msg = ( struct ofp_error_experimenter_msg * ) data->data;
uint32_t transaction_id = ntohl( experimenter_error_msg->header.xid );
uint16_t type = ntohs( experimenter_error_msg->type );
uint16_t exp_type = ntohs( experimenter_error_msg->exp_type );
uint32_t experimenter = ntohl( experimenter_error_msg->experimenter );
buffer *body = duplicate_buffer( data );
remove_front_buffer( body, offsetof( struct ofp_error_experimenter_msg, data ) );
debug( "An experimenter error message is received ( transaction_id = %#x, "
"type = %#x, exp_type = %#x, experimenter = %#x, data length = %u ).",
transaction_id, type, exp_type, experimenter, body->length );
if ( event_handlers.experimenter_error_callback == NULL ) {
debug( "Callback function for experimenter_error events is not set." );
free_buffer( body );
return;
}
debug( "Calling experimenter_error handler ( callback = %p, user_data = %p ).",
event_handlers.experimenter_error_callback, event_handlers.experimenter_error_user_data );
event_handlers.experimenter_error_callback( transaction_id,
type,
exp_type,
experimenter,
body,
event_handlers.experimenter_error_user_data );
free_buffer( body );
}
static void
handle_echo_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *header = data->data;
uint32_t transaction_id = htonl( header->xid );
uint16_t length = htons( header->length );
debug( "An echo request is received ( transaction_id = %#x, len = %u ).", transaction_id, length );
if ( event_handlers.echo_request_callback == NULL ) {
debug( "Callback function for echo request events is not set." );
return;
}
buffer *body = NULL;
if ( ( length - sizeof( struct ofp_header ) ) > 0 ) {
body = duplicate_buffer( data );
remove_front_buffer( body, sizeof( struct ofp_header ) );
}
debug( "Calling echo request handler ( callback = %p, body = %p, user_data = %p ).",
event_handlers.echo_request_callback,
body,
event_handlers.echo_request_user_data );
event_handlers.echo_request_callback( transaction_id, body, event_handlers.echo_request_user_data );
if ( body != NULL ) {
free_buffer( body );
}
}
static void
handle_echo_reply( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *header = data->data;
uint32_t transaction_id = htonl( header->xid );
uint16_t length = htons( header->length );
debug( "An echo reply is received ( transaction_id = %#x, len = %u ).", transaction_id, length );
if ( event_handlers.echo_reply_callback == NULL ) {
debug( "Callback function for echo reply events is not set." );
return;
}
buffer *body = NULL;
if ( ( length - sizeof( struct ofp_header ) ) > 0 ) {
body = duplicate_buffer( data );
remove_front_buffer( body, sizeof( struct ofp_header ) );
}
debug( "Calling echo reply handler ( callback = %p, body = %p, user_data = %p ).",
event_handlers.echo_reply_callback,
body,
event_handlers.echo_reply_user_data );
event_handlers.echo_reply_callback( transaction_id, body, event_handlers.echo_reply_user_data );
if ( body != NULL ) {
free_buffer( body );
}
}
static void
handle_experimenter( buffer *data ) {
assert( empty( data ) == false );
struct ofp_experimenter_header *experimenter_header = ( struct ofp_experimenter_header * ) data->data;
uint32_t transaction_id = ntohl( experimenter_header->header.xid );
uint32_t experimenter = ntohl( experimenter_header->experimenter );
uint32_t exp_type = ntohl( experimenter_header->exp_type );
uint16_t body_length = ( uint16_t ) ( ntohs( experimenter_header->header.length )
- sizeof( struct ofp_experimenter_header ) );
debug( "A experimenter message is received ( transaction_id = %#x, experimenter = %#x, "
"exp_type = %#x, body length = %u ).",
transaction_id, experimenter, exp_type, body_length );
if ( event_handlers.experimenter_callback == NULL ) {
debug( "Callback function for experimenter events is not set." );
return;
}
buffer *body = NULL;
if ( body_length > 0 ) {
body = duplicate_buffer( data );
remove_front_buffer( body, sizeof( struct ofp_experimenter_header ) );
}
debug( "Calling experimenter handler ( callback = %p, user_data = %p ).",
event_handlers.experimenter_callback, event_handlers.experimenter_user_data );
event_handlers.experimenter_callback( transaction_id,
experimenter,
exp_type,
body,
event_handlers.experimenter_user_data );
if ( body != NULL ) {
free_buffer( body );
}
}
static void
handle_features_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *header = data->data;
uint32_t transaction_id = ntohl( header->xid );
debug( "A features request is received ( transaction_id = %#x ).", transaction_id );
if ( event_handlers.features_request_callback == NULL ) {
debug( "Callback function for features request events is not set." );
return;
}
debug( "Calling features request handler ( callback = %p, user_data = %p ).",
event_handlers.features_request_callback,
event_handlers.features_request_user_data );
event_handlers.features_request_callback( transaction_id,
event_handlers.features_request_user_data );
}
static void
handle_get_config_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *header = data->data;
uint32_t transaction_id = ntohl( header->xid );
debug( "A get config request is received ( transaction_id = %#x ).", transaction_id );
if ( event_handlers.get_config_request_callback == NULL ) {
debug( "Callback function for get config request events is not set." );
return;
}
debug( "Calling get config request handler ( callback = %p, user_data = %p ).",
event_handlers.get_config_request_callback,
event_handlers.get_config_request_user_data );
event_handlers.get_config_request_callback( transaction_id,
event_handlers.get_config_request_user_data );
}
static void
handle_set_config( buffer *data ) {
assert( empty( data ) == false );
struct ofp_switch_config *config = data->data;
uint32_t transaction_id = ntohl( config->header.xid );
uint16_t flags = ntohs( config->flags );
uint16_t miss_send_len = ntohs( config->miss_send_len );
debug( "A set config is received ( transaction_id = %#x, flags = %#x, miss_send_len = %#x ).",
transaction_id, flags, miss_send_len );
if ( event_handlers.set_config_callback == NULL ) {
debug( "Callback function for set config events is not set." );
return;
}
debug( "Calling set config handler ( callback = %p, user_data = %p ).",
event_handlers.set_config_callback,
event_handlers.set_config_user_data );
event_handlers.set_config_callback( transaction_id, flags, miss_send_len,
event_handlers.set_config_user_data );
}
static void
handle_packet_out( buffer *data ) {
assert( empty( data ) == false );
struct ofp_packet_out *packet_out = data->data;
uint32_t transaction_id = ntohl( packet_out->header.xid );
uint32_t buffer_id = ntohl( packet_out->buffer_id );
uint32_t in_port = ntohl( packet_out->in_port );
size_t actions_len = ntohs( packet_out->actions_len );
openflow_actions *actions = NULL;
if ( actions_len > 0 ) {
actions = create_actions();
void *actions_p = packet_out->actions;
while ( actions_len > 0 ) {
struct ofp_action_header *ah = actions_p;
ntoh_action( ah, ah );
actions_len -= ah->len;
actions_p = ( char * ) actions_p + ah->len;
void *action = xmalloc( ah->len );
memcpy( action, ah, ah->len );
append_to_tail( &actions->list, ( void * ) action );
actions->n_actions++;
}
}
buffer *frame = NULL;
actions_len = ntohs( packet_out->actions_len );
size_t frame_length = ntohs( packet_out->header.length ) - offsetof( struct ofp_packet_out, actions ) - actions_len;
if ( frame_length > 0 ) {
frame = alloc_buffer_with_length( frame_length );
void *p = append_back_buffer( frame, frame_length );
size_t offset = offsetof( struct ofp_packet_out, actions ) + actions_len;
memcpy( p, ( char * ) packet_out + offset, frame_length );
}
debug( "A packet-out is received ( transaction_id = %#x, buffer_id = %#x, in_port = %#x, "
"actions_len = %u, frame_length = %u ).",
transaction_id, buffer_id, in_port, actions_len, frame_length );
if ( event_handlers.packet_out_callback == NULL ) {
debug( "Callback function for packet-out events is not set." );
goto END;
}
debug( "Calling packet-out handler ( callback = %p, user_data = %p ).",
event_handlers.packet_out_callback,
event_handlers.packet_out_user_data );
event_handlers.packet_out_callback( transaction_id, buffer_id, in_port, actions, frame,
event_handlers.packet_out_user_data );
END:
if ( actions != NULL ) {
delete_actions( actions );
}
if ( frame != NULL ) {
free_buffer( frame );
}
}
static void
handle_flow_mod( buffer *data ) {
assert( empty( data ) == false );
struct ofp_flow_mod *flow_mod = data->data;
uint32_t transaction_id = ntohl( flow_mod->header.xid );
uint64_t cookie = ntohll( flow_mod->cookie );
uint64_t cookie_mask = ntohll( flow_mod->cookie_mask );
uint8_t table_id = flow_mod->table_id;
uint8_t command = flow_mod->command;
uint16_t idle_timeout = ntohs( flow_mod->idle_timeout );
uint16_t hard_timeout = ntohs( flow_mod->hard_timeout );
uint16_t priority = ntohs( flow_mod->priority );
uint32_t buffer_id = ntohl( flow_mod->buffer_id );
uint32_t out_port = ntohl( flow_mod->out_port );
uint32_t out_group = ntohl( flow_mod->out_group );
uint16_t flags = ntohs( flow_mod->flags );
oxm_matches *match = parse_ofp_match( &flow_mod->match );
uint16_t match_len = ntohs( flow_mod->match.length );
match_len = ( uint16_t ) ( match_len + PADLEN_TO_64( match_len ) );
openflow_instructions *instructions = NULL;
uint16_t offset = ( uint16_t ) ( offsetof( struct ofp_flow_mod, match ) + match_len );
size_t instructions_length = ( uint16_t ) ( ntohs( flow_mod->header.length ) - offset );
if ( instructions_length > 0 ) {
instructions = create_instructions();
void *instruction_p = ( char * ) &flow_mod->match + match_len;
while ( instructions_length > 0 ) {
struct ofp_instruction *inst = instruction_p;
ntoh_instruction( inst, inst );
instructions_length -= inst->len;
instruction_p = ( char * ) instruction_p + inst->len;
void *instruction = xmalloc( inst->len );
memcpy( instruction, inst, inst->len );
append_to_tail( &instructions->list, ( void * ) instruction );
instructions->n_instructions++;
}
}
if ( get_logging_level() >= LOG_DEBUG ) {
char match_str[ MATCH_STRING_LENGTH ];
match_to_string( match, match_str, sizeof( match_str ) );
debug( "A flow modification is received ( transaction_id = %#x, cookie = %#" PRIx64 ", "
"cookie_mask = %#" PRIx64 ", table_id = %#x, command = %#x, idle_timeout = %#x, "
"hard_timeout = %#x, priority = %#x, buffer_id = %#x, "
"out_port = %#x, out_group = %#x, flags = %#x, match = [%s] ).",
transaction_id, cookie, cookie_mask, table_id, command,
idle_timeout, hard_timeout, priority, buffer_id,
out_port, out_group, flags, match_str );
}
if ( event_handlers.flow_mod_callback == NULL ) {
debug( "Callback function for flow modification events is not set." );
goto END;
}
debug( "Calling flow modification handler ( callback = %p, user_data = %p ).",
event_handlers.flow_mod_callback,
event_handlers.flow_mod_user_data );
event_handlers.flow_mod_callback( transaction_id, cookie, cookie_mask, table_id, command,
idle_timeout, hard_timeout, priority, buffer_id,
out_port, out_group, flags, match, instructions,
event_handlers.flow_mod_user_data );
END:
if ( match != NULL ) {
delete_oxm_matches( match );
}
if ( instructions != NULL ) {
delete_instructions( instructions );
}
}
static void
handle_group_mod( buffer *data ) {
assert( empty( data ) == false );
struct ofp_group_mod *group_mod = data->data;
list_element *buckets_head = NULL;
list_element *element = NULL;
struct ofp_bucket *bkt, *bucket;
uint32_t transaction_id = ntohl( group_mod->header.xid );
uint16_t command = ntohs( group_mod->command );
uint8_t type = group_mod->type;
uint32_t group_id = ntohl( group_mod->group_id );
size_t buckets_len = ntohs( group_mod->header.length ) - offsetof( struct ofp_group_mod, buckets );
debug( "A group modification is received ( transaction_id = %#x, command = %#x, "
"type = %#x, gruop_id = %#x, buckets length = %u ).",
transaction_id, command, type, group_id, buckets_len );
if ( event_handlers.group_mod_callback == NULL ) {
debug( "Callback function for group modification events is not set." );
return;
}
if ( buckets_len > 0 ) {
create_list( &buckets_head );
bucket = ( struct ofp_bucket * ) group_mod->buckets;
while ( buckets_len > 0 ) {
bkt = ( struct ofp_bucket * ) xcalloc( 1, ntohs( bucket->len ) );
ntoh_bucket( bkt, bucket );
append_to_tail( &buckets_head, bkt );
bucket = ( struct ofp_bucket * ) ( ( char * ) bucket + bkt->len );
buckets_len = ( uint16_t ) ( buckets_len - bkt->len );
}
}
debug( "Calling group modification handler ( callback = %p, user_data = %p ).",
event_handlers.group_mod_callback,
event_handlers.group_mod_user_data );
event_handlers.group_mod_callback( transaction_id, command, type, group_id, buckets_head,
event_handlers.group_mod_user_data );
if ( buckets_head != NULL ) {
element = buckets_head;
while ( element != NULL ) {
xfree( element->data );
element = element->next;
}
delete_list( buckets_head );
}
}
static void
handle_port_mod( buffer *data ) {
assert( empty( data ) == false );
struct ofp_port_mod *port_mod = data->data;
uint32_t transaction_id = ntohl( port_mod->header.xid );
uint32_t port_no = ntohl( port_mod->port_no );
uint8_t hw_addr[ OFP_ETH_ALEN ];
memcpy( hw_addr, port_mod->hw_addr, OFP_ETH_ALEN );
uint32_t config = ntohl( port_mod->config );
uint32_t mask = ntohl( port_mod->mask );
uint32_t advertise = ntohl( port_mod->advertise );
debug( "A port modification is received ( transaction_id = %#x, port_no = %#x, "
"hw_addr = %02x:%02x:%02x:%02x:%02x:%02x, config = %#x, mask = %#x, advertise = %#x ).",
transaction_id, port_no,
hw_addr[ 0 ], hw_addr[ 1 ], hw_addr[ 2 ], hw_addr[ 3 ], hw_addr[ 4 ], hw_addr[ 5 ],
config, mask, advertise );
if ( event_handlers.port_mod_callback == NULL ) {
debug( "Callback function for port modification events is not set." );
return;
}
debug( "Calling port modification handler ( callback = %p, user_data = %p ).",
event_handlers.port_mod_callback,
event_handlers.port_mod_user_data );
event_handlers.port_mod_callback( transaction_id, port_no, hw_addr, config, mask, advertise,
event_handlers.port_mod_user_data );
}
static void
handle_table_mod( buffer *data ) {
assert( empty( data ) == false );
struct ofp_table_mod *table_mod = data->data;
uint32_t transaction_id = ntohl( table_mod->header.xid );
uint8_t table_id = table_mod->table_id;
uint32_t config = ntohl( table_mod->config );
debug( "A table modification is received ( transaction_id = %#x, table_id = %#x, "
"config = %#x ).",
transaction_id, table_id, config );
if ( event_handlers.table_mod_callback == NULL ) {
debug( "Callback function for table modification events is not set." );
return;
}
debug( "Calling table modification handler ( callback = %p, user_data = %p ).",
event_handlers.table_mod_callback,
event_handlers.table_mod_user_data );
event_handlers.table_mod_callback( transaction_id, table_id, config,
event_handlers.table_mod_user_data );
}
static void
handle_multipart_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_multipart_request *multipart_request = data->data;
uint32_t transaction_id = ntohl( multipart_request->header.xid );
uint16_t type = ntohs( multipart_request->type );
uint16_t flags = ntohs( multipart_request->flags );
size_t body_length = ntohs( multipart_request->header.length ) - offsetof( struct ofp_multipart_request, body );
buffer *body = NULL;
if ( body_length > 0 ) {
body = alloc_buffer_with_length( body_length );
void *p = append_back_buffer( body, body_length );
memcpy( p, multipart_request->body, body_length );
switch ( type ) {
case OFPMP_FLOW:
{
struct ofp_flow_stats_request *flow = p;
flow->out_port = ntohl( flow->out_port );
flow->out_group = ntohl( flow->out_group );
flow->cookie = ntohll( flow->cookie );
flow->cookie_mask = ntohll( flow->cookie_mask );
ntoh_match( &flow->match, &flow->match );
}
break;
case OFPMP_AGGREGATE:
{
struct ofp_aggregate_stats_request *aggregate = p;
aggregate->out_port = ntohl( aggregate->out_port );
aggregate->out_group = ntohl( aggregate->out_group );
aggregate->cookie = ntohll( aggregate->cookie );
aggregate->cookie_mask = ntohll( aggregate->cookie_mask );
ntoh_match( &aggregate->match, &aggregate->match );
}
break;
case OFPMP_PORT_STATS:
{
struct ofp_port_stats_request *port = p;
port->port_no = ntohl( port->port_no );
}
break;
case OFPMP_QUEUE:
{
struct ofp_queue_stats_request *queue = p;
queue->port_no = ntohl( queue->port_no );
queue->queue_id = ntohl( queue->queue_id );
}
break;
case OFPMP_GROUP:
{
struct ofp_group_stats_request *group = p;
group->group_id = ntohl( group->group_id );
}
break;
case OFPMP_METER:
{
struct ofp_meter_multipart_request *meter = p;
meter->meter_id = ntohl( meter->meter_id );
}
break;
case OFPMP_METER_CONFIG:
{
struct ofp_meter_multipart_request *meter = p;
meter->meter_id = ntohl( meter->meter_id );
}
break;
case OFPMP_TABLE_FEATURES:
{
size_t rest_length = body_length;
void *rest_p = p;
while ( rest_length >= sizeof( struct ofp_table_features ) ) {
struct ofp_table_features *table = rest_p;
ntoh_table_features( table, table );
rest_p = ( char * ) rest_p + table->length;
rest_length -= table->length;
}
}
break;
case OFPMP_EXPERIMENTER:
{
struct ofp_experimenter_multipart_header *exp = p;
exp->experimenter = ntohl( exp->experimenter );
exp->exp_type = ntohl( exp->exp_type );
}
break;
default:
break;
}
}
debug( "A multipart request is received ( transaction_id = %#x, type = %#x, flags = %#x ).",
transaction_id, type, flags );
if ( event_handlers.multipart_request_callback == NULL ) {
debug( "Callback function for multipart request events is not set." );
return;
}
debug( "Calling multipart request handler ( callback = %p, user_data = %p ).",
event_handlers.multipart_request_callback,
event_handlers.multipart_request_user_data );
event_handlers.multipart_request_callback( transaction_id, type, flags, body,
event_handlers.multipart_request_user_data );
if ( body_length > 0 && body != NULL ) {
free_buffer( body );
}
}
static void
handle_barrier_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *header = data->data;
uint32_t transaction_id = ntohl( header->xid );
debug( "A barrier request is received ( transaction_id = %#x ).", transaction_id );
if ( event_handlers.barrier_request_callback == NULL ) {
debug( "Callback function for barrier request events is not set." );
return;
}
debug( "Calling barrier request handler ( callback = %p, user_data = %p ).",
event_handlers.barrier_request_callback,
event_handlers.barrier_request_user_data );
event_handlers.barrier_request_callback( transaction_id,
event_handlers.barrier_request_user_data );
}
static void
handle_queue_get_config_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_queue_get_config_request *queue_get_config_request = data->data;
uint32_t transaction_id = ntohl( queue_get_config_request->header.xid );
uint32_t port = ntohl( queue_get_config_request->port );
debug( "A queue get config request is received ( transaction_id = %#x, port = %#x ).",
transaction_id, port );
if ( event_handlers.queue_get_config_request_callback == NULL ) {
debug( "Callback function for queue get config request events is not set." );
return;
}
debug( "Calling queue get config request handler ( callback = %p, user_data = %p ).",
event_handlers.queue_get_config_request_callback,
event_handlers.queue_get_config_request_user_data );
event_handlers.queue_get_config_request_callback( transaction_id, port,
event_handlers.queue_get_config_request_user_data );
}
static void
handle_role_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_role_request *role_request = data->data;
uint32_t transaction_id = ntohl( role_request->header.xid );
uint32_t role = ntohl( role_request->role );
uint64_t generation_id = ntohll( role_request->generation_id );
debug( "A role request is received ( transaction_id = %#x, role = %#x, generation_id = %#" PRIx64 " ).",
transaction_id, role, generation_id );
if ( event_handlers.role_request_callback == NULL ) {
debug( "Callback function for role request events is not set." );
return;
}
debug( "Calling role request handler ( callback = %p, user_data = %p ).",
event_handlers.role_request_callback,
event_handlers.role_request_user_data );
event_handlers.role_request_callback( transaction_id, role, generation_id,
event_handlers.role_request_user_data );
}
static void
handle_get_async_request( buffer *data ) {
assert( empty( data ) == false );
struct ofp_header *get_async_request = data->data;
uint32_t transaction_id = ntohl( get_async_request->xid );
debug( "A get async request is received ( transaction_id = %#x ).",
transaction_id );
if ( event_handlers.get_async_request_callback == NULL ) {
debug( "Callback function for role request events is not set." );
return;
}
debug( "Calling get async request handler ( callback = %p, user_data = %p ).",
event_handlers.get_async_request_callback,
event_handlers.get_async_request_user_data );
event_handlers.get_async_request_callback( transaction_id,
event_handlers.get_async_request_user_data );
}
static void
handle_set_async( buffer *data ) {
assert( empty( data ) == false );
struct ofp_async_config *set_async = data->data;
uint32_t transaction_id = ntohl( set_async->header.xid );
uint32_t packet_in_mask[ 2 ];
uint32_t port_status_mask[ 2 ];
uint32_t flow_removed_mask[ 2 ];
packet_in_mask[ 0 ] = ntohl( set_async->packet_in_mask[ 0 ] );
packet_in_mask[ 1 ] = ntohl( set_async->packet_in_mask[ 1 ] );
port_status_mask[ 0 ] = ntohl( set_async->port_status_mask[ 0 ] );
port_status_mask[ 1 ] = ntohl( set_async->port_status_mask[ 1 ] );
flow_removed_mask[ 0 ] = ntohl( set_async->flow_removed_mask[ 0 ] );
flow_removed_mask[ 1 ] = ntohl( set_async->flow_removed_mask[ 1 ] );
debug( "A set async is received ( transaction_id = %#x, "
"packet_in_mask[0] = %#x, packet_in_mask[1] = %#x, "
"port_status_mask[0] = %#x, port_status_mask[1] = %#x, "
"flow_removed_mask[0] = %#x, flow_removed_mask[1] = %#x ).",
transaction_id, packet_in_mask[0], packet_in_mask[1],
port_status_mask[0], port_status_mask[1],
flow_removed_mask[0], flow_removed_mask[1] );
if ( event_handlers.set_async_callback == NULL ) {
debug( "Callback function for set async events is not set." );
return;
}
debug( "Calling set async handler ( callback = %p, user_data = %p ).",
event_handlers.set_async_callback,
event_handlers.set_async_user_data );
event_handlers.set_async_callback( transaction_id, packet_in_mask,
port_status_mask, flow_removed_mask,
event_handlers.set_async_user_data );
}
static void
handle_meter_mod( buffer *data ) {
assert( empty( data ) == false );
struct ofp_meter_mod *meter_mod = data->data;
list_element *meter_band_head = NULL;
list_element *element = NULL;
struct ofp_meter_band_header *mtbnd, *meter_band;
uint32_t transaction_id = ntohl( meter_mod->header.xid );
uint16_t command = ntohs( meter_mod->command );
uint16_t flags = ntohs( meter_mod->flags );
uint32_t meter_id = ntohl( meter_mod->meter_id );
size_t meter_band_len = ntohs( meter_mod->header.length ) - offsetof( struct ofp_meter_mod, bands );
debug( "A meter modification is received ( transaction_id = %#x, command = %#x, "
"flags = %#x, meter_id = %#x, bands length = %u ).",
transaction_id, command, flags, meter_id, meter_band_len );
if ( event_handlers.meter_mod_callback == NULL ) {
debug( "Callback function for meter modification events is not set." );
return;
}
if ( meter_band_len > 0 ) {
create_list( &meter_band_head );
meter_band = ( struct ofp_meter_band_header * ) meter_mod->bands;
while ( meter_band_len > 0 ) {
mtbnd = ( struct ofp_meter_band_header * ) xcalloc( 1, ntohs( meter_band->len ) );
ntoh_meter_band_header( mtbnd, meter_band );
append_to_tail( &meter_band_head, mtbnd );
meter_band = ( struct ofp_meter_band_header * ) ( ( char * ) meter_band + mtbnd->len );
meter_band_len = ( uint16_t ) ( meter_band_len - mtbnd->len );
}
}
debug( "Calling meter modification handler ( callback = %p, user_data = %p ).",
event_handlers.meter_mod_callback,
event_handlers.meter_mod_user_data );
event_handlers.meter_mod_callback( transaction_id, command, flags, meter_id, meter_band_head,
event_handlers.meter_mod_user_data );
if ( meter_band_head != NULL ) {
element = meter_band_head;
while ( element != NULL ) {
xfree( element->data );
element = element->next;
}
delete_list( meter_band_head );
}
}
static void
handle_controller_connected() {
if ( event_handlers.controller_connected_callback == NULL ) {
debug( "Callback function for controller connected events is not set." );
return;
}
event_handlers.controller_connected_callback( event_handlers.controller_connected_user_data );
}
static void
handle_controller_disconnected() {
if ( event_handlers.controller_disconnected_callback == NULL ) {
debug( "Callback function for controller disconnected events is not set." );
return;
}
event_handlers.controller_disconnected_callback( event_handlers.controller_disconnected_user_data );
}
static bool
handle_openflow_message( buffer *message ) {
debug( "An OpenFlow message is received from remote." );
assert( message != NULL );
assert( message->length >= sizeof( struct ofp_header ) );
struct ofp_header *header = ( struct ofp_header * ) message->data;
int ret = validate_openflow_message( message );
if ( ret < 0 ) {
error( "Failed to validate an OpenFlow message ( code = %d, length = %u ).", ret, message->length );
uint16_t type = OFPET_BAD_REQUEST;
uint16_t code = OFPBRC_EPERM;
get_error_type_and_code( header->type, ret, &type, &code );
send_error_message( ntohl( header->xid ), type, code );
return false;
}
ret = true;
switch ( header->type ) {
case OFPT_HELLO:
handle_hello( message );
break;
case OFPT_ERROR:
handle_error( message );
break;
case OFPT_ECHO_REQUEST:
handle_echo_request( message );
break;
case OFPT_ECHO_REPLY:
handle_echo_reply( message );
break;
case OFPT_EXPERIMENTER:
handle_experimenter( message);
break;
case OFPT_FEATURES_REQUEST:
handle_features_request( message );
break;
case OFPT_GET_CONFIG_REQUEST:
handle_get_config_request( message );
break;
case OFPT_SET_CONFIG:
handle_set_config( message );
break;
case OFPT_PACKET_OUT:
handle_packet_out( message );
break;
case OFPT_FLOW_MOD:
handle_flow_mod( message );
break;
case OFPT_GROUP_MOD:
handle_group_mod( message );
break;
case OFPT_PORT_MOD:
handle_port_mod( message );
break;
case OFPT_TABLE_MOD:
handle_table_mod( message );
break;
case OFPT_MULTIPART_REQUEST:
handle_multipart_request( message );
break;
case OFPT_BARRIER_REQUEST:
handle_barrier_request( message );
break;
case OFPT_QUEUE_GET_CONFIG_REQUEST:
handle_queue_get_config_request( message );
break;
case OFPT_ROLE_REQUEST:
handle_role_request( message );
break;
case OFPT_GET_ASYNC_REQUEST:
handle_get_async_request( message );
break;
case OFPT_SET_ASYNC:
handle_set_async( message );
break;
case OFPT_METER_MOD:
handle_meter_mod( message );
break;
default:
error( "Unhandled OpenFlow message ( type = %u ).", header->type );
ret = false;
break;
}
return ret;
}
static bool
send_openflow_message_to_secure_channel( buffer *message, void *user_data ) {
assert( user_data == NULL );
return send_message_to_secure_channel( message );
}
static bool
send_openflow_message_to_local( buffer *message, void *user_data ) {
char *service_name = user_data;
size_t service_name_length = strlen( service_name ) + 1;
size_t service_header_length = sizeof( openflow_service_header_t ) + service_name_length;
openflow_service_header_t *service_header = append_front_buffer( message, service_header_length );
service_header->service_name_length = htons( ( uint16_t ) service_name_length );
memcpy( ( char * ) service_header + sizeof( openflow_service_header_t ), service_name, service_name_length );
return send_message( service_name, MESSENGER_OPENFLOW_MESSAGE, message->data, message->length );
}
bool
switch_send_openflow_message( buffer *message ) {
assert( message != NULL );
assert( message->length >= sizeof( struct ofp_header ) );
struct ofp_header *header = message->data;
uint32_t transaction_id = ntohl( header->xid );
openflow_context *context = lookup_context( transaction_id );
if ( context != NULL ) {
assert( context->send_callback != NULL );
return context->send_callback( message, context->user_data );
}
return send_openflow_message_to_secure_channel( message, NULL );
}
bool
handle_secure_channel_message( buffer *message ) {
assert( message != NULL );
assert( message->length >= sizeof( struct ofp_header ) );
debug( "A message is received from remote ( length = %u ).", message->length );
struct ofp_header *header = message->data;
save_context( ntohl( header->xid ), message, send_openflow_message_to_secure_channel, NULL );
return handle_openflow_message( message );
}
static void
handle_local_message( uint16_t tag, void *data, size_t length ) {
assert( data != NULL );
assert( length >= sizeof( openflow_service_header_t ) );
debug( "A message is received from local ( tag = %u, data = %p, length = %u ).", tag, data, length );
switch ( tag ) {
case MESSENGER_OPENFLOW_MESSAGE:
{
openflow_service_header_t *header = data;
uint16_t service_name_length = ntohs( header->service_name_length );
size_t ofp_offset = sizeof( openflow_service_header_t ) + service_name_length;
size_t ofp_length = length - ofp_offset;
buffer *message = alloc_buffer_with_length( ofp_length );
char *p = append_back_buffer( message, ofp_length );
memcpy( p, ( char * ) data + ofp_offset, ofp_length );
char *service_name = strndup( ( char * ) data + sizeof( openflow_service_header_t ), service_name_length );
save_context( ntohl( ( ( struct ofp_header * ) p )->xid ), message, send_openflow_message_to_local,
service_name );
handle_openflow_message( message );
}
break;
default:
break;
}
}
bool
init_openflow_switch_interface( const uint64_t datapath_id, uint32_t controller_ip, uint16_t controller_port ) {
debug( "Initializing OpenFlow Switch Interface ( datapath_id = %#" PRIx64 ", controller_ip = %#x, controller_port = %u ).",
datapath_id, controller_ip, controller_port );
if ( openflow_switch_interface_is_initialized() ) {
error( "OpenFlow Switch Interface is already initialized." );
return false;
}
bool ret = init_secure_channel( controller_ip, controller_port,
handle_controller_connected, handle_controller_disconnected );
if ( ret == false ) {
error( "Failed to initialize a secure chanel." );
return false;
}
memset( &event_handlers, 0, sizeof( openflow_switch_event_handlers ) );
memset( &config, 0, sizeof( openflow_switch_config ) );
config.datapath_id = datapath_id;
config.controller.ip = controller_ip;
config.controller.port = controller_port;
init_context();
add_periodic_event_callback_safe( 5, age_contexts, NULL );
add_message_received_callback( "switch", handle_local_message );
openflow_switch_interface_initialized = true;
return true;
}
bool
finalize_openflow_switch_interface() {
if ( !openflow_switch_interface_is_initialized() ) {
error( "OpenFlow Switch Interface is not initialized." );
return false;
}
finalize_secure_channel();
delete_timer_event_safe( age_contexts, NULL );
finalize_context();
openflow_switch_interface_initialized = false;
return true;
}
static const buffer *
get_openflow_message( uint32_t transaction_id ) {
openflow_context *context = lookup_context( transaction_id );
if ( context == NULL ) {
return NULL;
}
return context->message;
}
bool
send_error_message( uint32_t transaction_id, uint16_t type, uint16_t code ) {
buffer *data = NULL;
switch ( type ) {
case OFPET_HELLO_FAILED:
{
switch ( code ) {
case OFPHFC_INCOMPATIBLE:
{
const char *description = "Incompatible OpenFlow version.";
size_t length = strlen( description ) + 1;
data = alloc_buffer_with_length ( length );
void *p = append_back_buffer( data, length );
strncpy( p, description, length );
}
break;
case OFPHFC_EPERM:
{
const char *description = "Permissions error.";
size_t length = strlen( description ) + 1;
data = alloc_buffer_with_length ( length );
void *p = append_back_buffer( data, length );
strncpy( p, description, length );
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_BAD_REQUEST:
{
switch ( code ) {
case OFPBRC_BAD_VERSION:
case OFPBRC_BAD_TYPE:
case OFPBRC_BAD_MULTIPART:
case OFPBRC_BAD_EXPERIMENTER:
case OFPBRC_BAD_EXP_TYPE:
case OFPBRC_EPERM:
case OFPBRC_BAD_LEN:
case OFPBRC_BUFFER_EMPTY:
case OFPBRC_BUFFER_UNKNOWN:
case OFPBRC_BAD_TABLE_ID:
case OFPBRC_IS_SLAVE:
case OFPBRC_BAD_PORT:
case OFPBRC_BAD_PACKET:
case OFPBRC_MULTIPART_BUFFER_OVERFLOW:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_BAD_ACTION:
{
switch ( code ) {
case OFPBAC_BAD_TYPE:
case OFPBAC_BAD_LEN:
case OFPBAC_BAD_EXPERIMENTER:
case OFPBAC_BAD_EXP_TYPE:
case OFPBAC_BAD_OUT_PORT:
case OFPBAC_BAD_ARGUMENT:
case OFPBAC_EPERM:
case OFPBAC_TOO_MANY:
case OFPBAC_BAD_QUEUE:
case OFPBAC_BAD_OUT_GROUP:
case OFPBAC_MATCH_INCONSISTENT:
case OFPBAC_UNSUPPORTED_ORDER:
case OFPBAC_BAD_TAG:
case OFPBAC_BAD_SET_TYPE:
case OFPBAC_BAD_SET_LEN:
case OFPBAC_BAD_SET_ARGUMENT:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_BAD_INSTRUCTION:
{
switch ( code ) {
case OFPBIC_UNKNOWN_INST:
case OFPBIC_UNSUP_INST:
case OFPBIC_BAD_TABLE_ID:
case OFPBIC_UNSUP_METADATA:
case OFPBIC_UNSUP_METADATA_MASK:
case OFPBIC_BAD_EXPERIMENTER:
case OFPBIC_BAD_EXP_TYPE:
case OFPBIC_BAD_LEN:
case OFPBIC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_BAD_MATCH:
{
switch ( code ) {
case OFPBMC_BAD_TYPE:
case OFPBMC_BAD_LEN:
case OFPBMC_BAD_TAG:
case OFPBMC_BAD_DL_ADDR_MASK:
case OFPBMC_BAD_NW_ADDR_MASK:
case OFPBMC_BAD_WILDCARDS:
case OFPBMC_BAD_FIELD:
case OFPBMC_BAD_VALUE:
case OFPBMC_BAD_MASK:
case OFPBMC_BAD_PREREQ:
case OFPBMC_DUP_FIELD:
case OFPBMC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_FLOW_MOD_FAILED:
{
switch ( code ) {
case OFPFMFC_UNKNOWN:
case OFPFMFC_TABLE_FULL:
case OFPFMFC_BAD_TABLE_ID:
case OFPFMFC_OVERLAP:
case OFPFMFC_EPERM:
case OFPFMFC_BAD_TIMEOUT:
case OFPFMFC_BAD_COMMAND:
case OFPFMFC_BAD_FLAGS:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_GROUP_MOD_FAILED:
{
switch ( code ) {
case OFPGMFC_GROUP_EXISTS:
case OFPGMFC_INVALID_GROUP:
case OFPGMFC_WEIGHT_UNSUPPORTED:
case OFPGMFC_OUT_OF_GROUPS:
case OFPGMFC_OUT_OF_BUCKETS:
case OFPGMFC_CHAINING_UNSUPPORTED:
case OFPGMFC_WATCH_UNSUPPORTED:
case OFPGMFC_LOOP:
case OFPGMFC_UNKNOWN_GROUP:
case OFPGMFC_CHAINED_GROUP:
case OFPGMFC_BAD_TYPE:
case OFPGMFC_BAD_COMMAND:
case OFPGMFC_BAD_BUCKET:
case OFPGMFC_BAD_WATCH:
case OFPGMFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_PORT_MOD_FAILED:
{
switch ( code ) {
case OFPPMFC_BAD_PORT:
case OFPPMFC_BAD_HW_ADDR:
case OFPPMFC_BAD_CONFIG:
case OFPPMFC_BAD_ADVERTISE:
case OFPPMFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_TABLE_MOD_FAILED:
{
switch ( code ) {
case OFPTMFC_BAD_TABLE:
case OFPTMFC_BAD_CONFIG:
case OFPTMFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_QUEUE_OP_FAILED:
{
switch ( code ) {
case OFPQOFC_BAD_PORT:
case OFPQOFC_BAD_QUEUE:
case OFPQOFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_SWITCH_CONFIG_FAILED:
{
switch ( code ) {
case OFPSCFC_BAD_FLAGS:
case OFPSCFC_BAD_LEN:
case OFPSCFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_ROLE_REQUEST_FAILED:
{
switch ( code ) {
case OFPRRFC_STALE:
case OFPRRFC_UNSUP:
case OFPRRFC_BAD_ROLE:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_METER_MOD_FAILED:
{
switch ( code ) {
case OFPMMFC_UNKNOWN:
case OFPMMFC_METER_EXISTS:
case OFPMMFC_INVALID_METER:
case OFPMMFC_UNKNOWN_METER:
case OFPMMFC_BAD_COMMAND:
case OFPMMFC_BAD_FLAGS:
case OFPMMFC_BAD_RATE:
case OFPMMFC_BAD_BURST:
case OFPMMFC_BAD_BAND:
case OFPMMFC_BAD_BAND_VALUE:
case OFPMMFC_OUT_OF_METERS:
case OFPMMFC_OUT_OF_BANDS:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
case OFPET_TABLE_FEATURES_FAILED:
{
switch ( code ) {
case OFPTFFC_BAD_TABLE:
case OFPTFFC_BAD_METADATA:
case OFPTFFC_BAD_TYPE:
case OFPTFFC_BAD_LEN:
case OFPTFFC_BAD_ARGUMENT:
case OFPTFFC_EPERM:
{
const buffer *original_message = get_openflow_message( transaction_id );
if ( original_message != NULL ) {
data = duplicate_buffer( original_message );
if ( data->length > 64 ) {
data->length = 64;
}
}
}
break;
default:
error( "Undefined error code ( type = %#x, code = %#x ).", type, code );
return false;
}
}
break;
default:
error( "Undefined error type ( type = %#x, code = %#x ).", type, code );
return false;
}
buffer *err = create_error( transaction_id, type, code, data );
bool ret = switch_send_openflow_message( err );
if ( !ret ) {
error( "Failed to send an error message ( transaction_id = %#x, type = %#x, code = %#x ).",
transaction_id, type, code );
}
free_buffer( err );
if ( data != NULL ) {
free_buffer( data );
}
return ret;
}
/*
* Local variables:
* c-basic-offset: 2
* indent-tabs-mode: nil
* End:
*/