/* * Author: Yasunobu Chiba * * Copyright (C) 2008-2012 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 #include #include #include #include #include #include #include #include "openflow_message.h" #include "packet_info.h" #include "wrapper.h" #include "log.h" #ifdef UNIT_TESTING // Allow static functions to be called from unit tests. #define static /* Redirect getpid to a function in the test application so it's * possible to test if pid value is correctly used. */ #ifdef getpid #undef getpid #endif // getpid #define getpid mock_getpid extern pid_t mock_getpid( void ); /* Redirect debug to a function in the test application so it's * possible to test if debug messages are generated expectedly. */ #ifdef debug #undef debug #endif // debug #define debug mock_debug extern void mock_debug( const char *format, ... ); #endif // UNIT_TESTING #define VLAN_VID_MASK 0x0fff // 12 bits #define VLAN_PCP_MASK 0x07 // 3 bits #define NW_TOS_MASK 0xfc // upper 6 bits #define ARP_OP_MASK 0x00ff // 8 bits #define PORT_CONFIG ( OFPPC_PORT_DOWN | OFPPC_NO_STP | OFPPC_NO_RECV \ | OFPPC_NO_RECV_STP | OFPPC_NO_FLOOD | OFPPC_NO_FWD \ | OFPPC_NO_PACKET_IN ) #define PORT_STATE ( OFPPS_LINK_DOWN | OFPPS_STP_LISTEN | OFPPS_STP_LEARN \ | OFPPS_STP_FORWARD | OFPPS_STP_BLOCK | OFPPS_STP_MASK ) #define PORT_FEATURES ( OFPPF_10MB_HD | OFPPF_10MB_FD | OFPPF_100MB_HD \ | OFPPF_100MB_FD | OFPPF_1GB_HD | OFPPF_1GB_FD \ | OFPPF_10GB_FD | OFPPF_COPPER | OFPPF_FIBER \ | OFPPF_AUTONEG | OFPPF_PAUSE | OFPPF_PAUSE_ASYM ) #define FLOW_MOD_FLAGS ( OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF_EMERG ) static uint32_t transaction_id = 0; static pthread_mutex_t transaction_id_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static uint64_t cookie = 0; static pthread_mutex_t cookie_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; bool init_openflow_message( void ) { pid_t pid; pid = getpid(); pthread_mutex_lock( &transaction_id_mutex ); transaction_id = ( uint32_t ) pid << 16; pthread_mutex_unlock( &transaction_id_mutex ); pthread_mutex_lock( &cookie_mutex ); cookie = ( uint64_t ) pid << 48; pthread_mutex_unlock( &cookie_mutex ); debug( "transaction_id and cookie are initialized ( transaction_id = %#x, cookie = %#" PRIx64 " ).", transaction_id, cookie ); return true; } static buffer * create_header( const uint32_t transaction_id, const uint8_t type, const uint16_t length ) { debug( "Creating an OpenFlow header (version = %#x, type = %#x, length = %u, xid = %#x).", OFP_VERSION, type, length, transaction_id ); assert( length >= sizeof( struct ofp_header ) ); buffer *buffer = alloc_buffer(); assert( buffer != NULL ); struct ofp_header *header = append_back_buffer( buffer, length ); assert( header != NULL ); memset( header, 0, length ); header->version = OFP_VERSION; header->type = type; header->length = htons( length ); header->xid = htonl( transaction_id ); return buffer; } buffer * create_error( const uint32_t transaction_id, const uint16_t type, const uint16_t code, const buffer *data ) { uint16_t length; uint16_t data_len = 0; buffer *buffer; struct ofp_error_msg *error_msg; if ( ( data != NULL ) && ( data->length > 0 ) ) { data_len = ( uint16_t ) data->length; } debug( "Creating an error ( xid = %#x, type = %#x, code = %#x, data length = %u ).", transaction_id, type, code, data_len ); length = ( uint16_t ) ( sizeof( struct ofp_error_msg ) + data_len ); buffer = create_header( transaction_id, OFPT_ERROR, length ); assert( buffer != NULL ); error_msg = ( struct ofp_error_msg * ) buffer->data; error_msg->type = htons( type ); error_msg->code = htons( code ); if ( data_len > 0 ) { memcpy( error_msg->data, data->data, data->length ); } return buffer; } buffer * create_hello( const uint32_t transaction_id ) { debug( "Creating a hello ( xid = %#x ).", transaction_id ); return create_header( transaction_id, OFPT_HELLO, sizeof( struct ofp_header ) ); } buffer * create_echo_request( const uint32_t transaction_id, const buffer *body ) { uint16_t data_length = 0; if ( ( body != NULL ) && ( body->length > 0 ) ) { data_length = ( uint16_t ) body->length; } debug( "Creating an echo request ( xid = %#x, data length = %u ).", transaction_id, data_length ); buffer *echo_request = create_header( transaction_id, OFPT_ECHO_REQUEST, ( uint16_t ) ( sizeof( struct ofp_header ) + data_length ) ); assert( echo_request != NULL ); if ( data_length > 0 ) { memcpy( ( char * ) echo_request->data + sizeof( struct ofp_header ), body->data, data_length ); } return echo_request; } buffer * create_echo_reply( const uint32_t transaction_id, const buffer *body ) { uint16_t data_length = 0; if ( ( body != NULL ) && ( body->length > 0 ) ) { data_length = ( uint16_t ) body->length; } debug( "Creating an echo reply ( xid = %#x, data length = %u ).", transaction_id, data_length ); buffer *echo_reply = create_header( transaction_id, OFPT_ECHO_REPLY, ( uint16_t ) ( sizeof( struct ofp_header ) + data_length ) ); assert( echo_reply != NULL ); if ( data_length > 0 ) { memcpy( ( char * ) echo_reply->data + sizeof( struct ofp_header ), body->data, data_length ); } return echo_reply; } buffer * create_vendor( const uint32_t transaction_id, const uint32_t vendor, const buffer *data ) { void *d; uint16_t length; uint16_t data_length = 0; buffer *buffer; struct ofp_vendor_header *vendor_header; if ( ( data != NULL ) && ( data->length > 0 ) ) { data_length = ( uint16_t ) data->length; } debug( "Creating a vendor ( xid = %#x, vendor = %#x, data length = %u ).", transaction_id, vendor, data_length ); length = ( uint16_t ) ( sizeof( struct ofp_vendor_header ) + data_length ); buffer = create_header( transaction_id, OFPT_VENDOR, length ); assert( buffer != NULL ); vendor_header = ( struct ofp_vendor_header * ) buffer->data; vendor_header->vendor = htonl( vendor ); if ( data_length > 0 ) { d = ( void * ) ( ( char * ) buffer->data + sizeof( struct ofp_vendor_header ) ); memcpy( d, data->data, data_length ); } return buffer; } buffer * create_features_request( const uint32_t transaction_id ) { debug( "Creating a features request ( xid = %#x ).", transaction_id ); return create_header( transaction_id, OFPT_FEATURES_REQUEST, sizeof( struct ofp_header ) ); } buffer * create_features_reply( const uint32_t transaction_id, const uint64_t datapath_id, const uint32_t n_buffers, const uint8_t n_tables, const uint32_t capabilities, const uint32_t actions, const list_element *ports ) { char port_str[ 1024 ]; int count = 0; uint16_t n_ports = 0; buffer *buffer; struct ofp_switch_features *switch_features; struct ofp_phy_port *phy_port; struct ofp_phy_port pn; list_element *p = NULL, *port; debug( "Creating a features reply " "( xid = %#x, datapath_id = %#" PRIx64 ", n_buffers = %u, n_tables = %u, capabilities = %#x, actions = %#x ).", transaction_id, datapath_id, n_buffers, n_tables, capabilities, actions ); if ( ports != NULL ) { p = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( p, ports, sizeof( list_element ) ); port = p; while ( port != NULL ) { port = port->next; n_ports++; } } debug( "# of ports = %u.", n_ports ); buffer = create_header( transaction_id, OFPT_FEATURES_REPLY, ( uint16_t ) ( offsetof( struct ofp_switch_features, ports ) + sizeof( struct ofp_phy_port ) * n_ports ) ); assert( buffer != NULL ); switch_features = ( struct ofp_switch_features * ) buffer->data; switch_features->datapath_id = htonll( datapath_id ); switch_features->n_buffers = htonl( n_buffers ); switch_features->n_tables = n_tables; memset( switch_features->pad, 0, sizeof( switch_features->pad ) ); switch_features->capabilities = htonl( capabilities ); switch_features->actions = htonl( actions ); if ( n_ports ) { phy_port = ( struct ofp_phy_port * ) ( ( char * ) buffer->data + offsetof( struct ofp_switch_features, ports) ); port = p; while ( port != NULL ) { phy_port_to_string( port->data, port_str, sizeof( port_str ) ); debug( "[%u] %s", count++, port_str ); hton_phy_port( &pn, ( struct ofp_phy_port * ) port->data ); memcpy( phy_port, &pn, sizeof( struct ofp_phy_port ) ); port = port->next; phy_port++; } xfree( p ); } return buffer; } buffer * create_get_config_request( const uint32_t transaction_id ) { debug( "Creating a get config request ( xid = %#x ).", transaction_id ); return create_header( transaction_id, OFPT_GET_CONFIG_REQUEST, sizeof( struct ofp_header ) ); } buffer * create_get_config_reply( const uint32_t transaction_id, const uint16_t flags, const uint16_t miss_send_len ) { buffer *buffer; struct ofp_switch_config *switch_config; debug( "Creating a get config reply ( xid = %#x, flags = %#x, miss_send_len = %u ).", transaction_id, flags, miss_send_len ); buffer = create_header( transaction_id, OFPT_GET_CONFIG_REPLY, sizeof( struct ofp_switch_config ) ); assert( buffer != NULL ); switch_config = ( struct ofp_switch_config * ) buffer->data; switch_config->flags = htons( flags ); switch_config->miss_send_len = htons( miss_send_len ); return buffer; } buffer * create_set_config( const uint32_t transaction_id, const uint16_t flags, uint16_t miss_send_len ) { debug( "Creating a set config ( xid = %#x, flags = %#x, miss_send_len = %u ).", transaction_id, flags, miss_send_len ); buffer *set_config = create_header( transaction_id, OFPT_SET_CONFIG, sizeof( struct ofp_switch_config ) ); assert( set_config != NULL ); struct ofp_switch_config *switch_config = ( struct ofp_switch_config * ) set_config->data; switch_config->flags = htons( flags ); switch_config->miss_send_len = htons( miss_send_len ); return set_config; } buffer * create_packet_in( const uint32_t transaction_id, const uint32_t buffer_id, const uint16_t total_len, uint16_t in_port, const uint8_t reason, const buffer *data ) { uint16_t data_length = 0; buffer *buffer; struct ofp_packet_in *packet_in; if ( ( data != NULL ) && ( data->length > 0 ) ) { data_length = ( uint16_t ) data->length; } debug( "Creating a packet-in " "( xid = %#x, buffer_id = %#x, total_len = %u, " "in_port = %u, reason = %#x, data length = %u ).", transaction_id, buffer_id, total_len, in_port, reason, data_length ); buffer = create_header( transaction_id, OFPT_PACKET_IN, ( uint16_t ) ( offsetof( struct ofp_packet_in, data ) + data_length ) ); assert( buffer != NULL ); packet_in = ( struct ofp_packet_in * ) buffer->data; packet_in->buffer_id = htonl( buffer_id ); packet_in->total_len = htons( total_len ); packet_in->in_port = htons( in_port ); packet_in->reason = reason; packet_in->pad = 0; if ( data_length > 0 ) { memcpy( packet_in->data, data->data, data_length ); } return buffer; } buffer * create_flow_removed( const uint32_t transaction_id, const struct ofp_match match, const uint64_t cookie, const uint16_t priority, const uint8_t reason, const uint32_t duration_sec, const uint32_t duration_nsec, const uint16_t idle_timeout, const uint64_t packet_count, const uint64_t byte_count ) { char match_str[ 1024 ]; buffer *buffer; struct ofp_match m = match; struct ofp_flow_removed *flow_removed; // Because match_to_string() is costly, we check logging_level first. if ( get_logging_level() >= LOG_DEBUG ) { match_to_string( &m, match_str, sizeof( match_str ) ); debug( "Creating a flow removed " "( xid = %#x, match = [%s], cookie = %#" PRIx64 ", priority = %u, " "reason = %#x, duration_sec = %u, duration_nsec = %u, " "idle_timeout = %u, packet_count = %" PRIu64 ", byte_count = %" PRIu64 " ).", transaction_id, match_str, cookie, priority, reason, duration_sec, duration_nsec, idle_timeout, packet_count, byte_count ); } buffer = create_header( transaction_id, OFPT_FLOW_REMOVED, sizeof( struct ofp_flow_removed ) ); assert( buffer != NULL ); flow_removed = ( struct ofp_flow_removed * ) buffer->data; hton_match( &flow_removed->match, &m ); flow_removed->cookie = htonll( cookie ); flow_removed->priority = htons( priority ); flow_removed->reason = reason; memset( &flow_removed->pad, 0, sizeof( flow_removed->pad ) ); flow_removed->duration_sec = htonl( duration_sec ); flow_removed->duration_nsec = htonl( duration_nsec ); flow_removed->idle_timeout = htons( idle_timeout ); memset( &flow_removed->pad2, 0, sizeof( flow_removed->pad2 ) ); flow_removed->packet_count = htonll( packet_count ); flow_removed->byte_count = htonll( byte_count ); return buffer; } buffer * create_port_status( const uint32_t transaction_id, const uint8_t reason, const struct ofp_phy_port desc) { char desc_str[ 1024 ]; buffer *buffer; struct ofp_phy_port d = desc; struct ofp_port_status *port_status; phy_port_to_string( &d, desc_str, sizeof( desc_str ) ); debug( "Creating a port status ( xid = %#x, reason = %#x, desc = [%s] ).", transaction_id, reason, desc_str ); buffer = create_header( transaction_id, OFPT_PORT_STATUS, sizeof( struct ofp_port_status ) ); assert( buffer != NULL ); port_status = ( struct ofp_port_status * ) buffer->data; port_status->reason = reason; memset( &port_status->pad, 0, sizeof( port_status->pad ) ); hton_phy_port( &port_status->desc, &d ); return buffer; } static uint16_t get_actions_length( const openflow_actions *actions ) { int actions_length = 0; struct ofp_action_header *action_header; list_element *action; debug( "Calculating the total length of actions." ); assert( actions != NULL ); action = actions->list; while ( action != NULL ) { action_header = ( struct ofp_action_header * ) action->data; if ( ( action_header->type <= OFPAT_ENQUEUE ) || ( action_header->type == OFPAT_VENDOR ) ) { actions_length += action_header->len; } else { critical( "Undefined action type ( type = %#x ).", action_header->type ); assert( 0 ); } action = action->next; } debug( "Total length of actions = %u.", actions_length ); if ( actions_length > UINT16_MAX ) { critical( "Too many actions ( # of actions = %d, actions length = %u ).", actions->n_actions, actions_length ); assert( 0 ); } return ( uint16_t ) actions_length; } buffer * create_packet_out( const uint32_t transaction_id, const uint32_t buffer_id, const uint16_t in_port, const openflow_actions *actions, const buffer *data ) { void *a, *d; uint16_t length; uint16_t data_length = 0; uint16_t action_length = 0; uint16_t actions_length = 0; buffer *buffer; struct ofp_packet_out *packet_out; struct ofp_action_header *action_header; list_element *action; if ( ( data != NULL ) && ( data->length > 0 ) ) { data_length = ( uint16_t ) data->length; } debug( "Creating a packet-out ( xid = %#x, buffer_id = %#x, in_port = %u, data length = %u ).", transaction_id, buffer_id, in_port, data_length ); if ( buffer_id == UINT32_MAX ) { if ( data == NULL ) { die( "An Ethernet frame must be provided if buffer_id is equal to %#x", UINT32_MAX ); } if ( data->length + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH ) { die( "The length of the provided Ethernet frame is shorter than the minimum length of an Ethernet frame (= %d bytes).", ETH_MINIMUM_LENGTH ); } } if ( actions != NULL ) { debug( "# of actions = %d.", actions->n_actions ); actions_length = get_actions_length( actions ); } length = ( uint16_t ) ( offsetof( struct ofp_packet_out, actions ) + actions_length + data_length ); buffer = create_header( transaction_id, OFPT_PACKET_OUT, length ); assert( buffer != NULL ); packet_out = ( struct ofp_packet_out * ) buffer->data; packet_out->buffer_id = htonl( buffer_id ); packet_out->in_port = htons( in_port ); packet_out->actions_len = htons( actions_length ); if ( actions_length > 0 ) { a = ( void * ) ( ( char * ) buffer->data + offsetof( struct ofp_packet_out, actions ) ); action = actions->list; while ( action != NULL ) { action_header = ( struct ofp_action_header * ) action->data; action_length = action_header->len; hton_action( ( struct ofp_action_header * ) a, action_header ); a = ( void * ) ( ( char * ) a + action_length ); action = action->next; } } if ( data_length > 0 ) { d = ( void * ) ( ( char * ) buffer->data + offsetof( struct ofp_packet_out, actions ) + actions_length ); memcpy( d, data->data, data_length ); } return buffer; } buffer * create_flow_mod( const uint32_t transaction_id, const struct ofp_match match, const uint64_t cookie, const uint16_t command, const uint16_t idle_timeout, const uint16_t hard_timeout, const uint16_t priority, const uint32_t buffer_id, const uint16_t out_port, const uint16_t flags, const openflow_actions *actions ) { void *a; char match_str[ 1024 ]; uint16_t length; uint16_t action_length = 0; uint16_t actions_length = 0; buffer *buffer; struct ofp_match m = match; struct ofp_flow_mod *flow_mod; struct ofp_action_header *action_header; list_element *action; // Because match_to_string() is costly, we check logging_level first. if ( get_logging_level() >= LOG_DEBUG ) { match_to_string( &m, match_str, sizeof( match_str ) ); debug( "Creating a flow modification " "( xid = %#x, match = [%s], cookie = %#" PRIx64 ", command = %#x, " "idle_timeout = %u, hard_timeout = %u, priority = %u, " "buffer_id = %#x, out_port = %u, flags = %#x ).", transaction_id, match_str, cookie, command, idle_timeout, hard_timeout, priority, buffer_id, out_port, flags ); } if ( actions != NULL ) { debug( "# of actions = %d.", actions->n_actions ); actions_length = get_actions_length( actions ); } length = ( uint16_t ) ( offsetof( struct ofp_flow_mod, actions ) + actions_length ); buffer = create_header( transaction_id, OFPT_FLOW_MOD, length ); assert( buffer != NULL ); flow_mod = ( struct ofp_flow_mod * ) buffer->data; hton_match( &flow_mod->match, &m ); flow_mod->cookie = htonll( cookie ); flow_mod->command = htons( command ); flow_mod->idle_timeout = htons( idle_timeout ); flow_mod->hard_timeout = htons( hard_timeout ); flow_mod->priority = htons( priority ); flow_mod->buffer_id = htonl( buffer_id ); flow_mod->out_port = htons( out_port ); flow_mod->flags = htons( flags ); if ( actions_length > 0 ) { a = ( void * ) ( ( char * ) buffer->data + offsetof( struct ofp_flow_mod, actions ) ); action = actions->list; while ( action != NULL ) { action_header = ( struct ofp_action_header * ) action->data; action_length = action_header->len; hton_action( ( struct ofp_action_header * ) a, action_header ); a = ( void * ) ( ( char * ) a + action_length ); action = action->next; } } return buffer; } buffer * create_port_mod( const uint32_t transaction_id, const uint16_t port_no, const uint8_t hw_addr[ OFP_ETH_ALEN ], const uint32_t config, const uint32_t mask, const uint32_t advertise ) { buffer *buffer; struct ofp_port_mod *port_mod; debug( "Creating a port modification " "( xid = %#x, port_no = %u, 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 ); buffer = create_header( transaction_id, OFPT_PORT_MOD, sizeof( struct ofp_port_mod ) ); assert( buffer != NULL ); port_mod = ( struct ofp_port_mod * ) buffer->data; port_mod->port_no = htons( port_no ); memcpy( port_mod->hw_addr, hw_addr, OFP_ETH_ALEN ); port_mod->config = htonl( config ); port_mod->mask = htonl( mask ); port_mod->advertise = htonl( advertise ); memset( port_mod->pad, 0, sizeof( port_mod->pad ) ); return buffer; } static buffer * create_stats_request( const uint32_t transaction_id, const uint16_t type, const uint16_t length, const uint16_t flags ) { buffer *buffer; struct ofp_stats_request *stats_request; debug( "Creating a stats request ( xid = %#x, type = %#x, length = %u, flags = %#x ).", transaction_id, type, length, flags ); buffer = create_header( transaction_id, OFPT_STATS_REQUEST, length ); assert( buffer != NULL ); stats_request = ( struct ofp_stats_request * ) buffer->data; stats_request->type = htons( type ); stats_request->flags = htons( flags ); return buffer; } buffer * create_desc_stats_request( const uint32_t transaction_id, const uint16_t flags ) { debug( "Creating a description stats request ( xid = %#x, flags = %#x ).", transaction_id, flags ); return create_stats_request( transaction_id, OFPST_DESC, sizeof( struct ofp_stats_request ), flags ); } buffer * create_flow_stats_request( const uint32_t transaction_id, const uint16_t flags, const struct ofp_match match, const uint8_t table_id, const uint16_t out_port ) { char match_str[ 1024 ]; uint16_t length; buffer *buffer; struct ofp_match m = match; struct ofp_flow_stats_request *flow_stats_request; // Because match_to_string() is costly, we check logging_level first. if ( get_logging_level() >= LOG_DEBUG ) { match_to_string( &m, match_str, sizeof( match_str ) ); debug( "Creating a flow stats request ( xid = %#x, flags = %#x, match = [%s], table_id = %u, out_port = %u ).", transaction_id, flags, match_str, table_id, out_port ); } length = ( uint16_t ) ( offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_flow_stats_request ) ); buffer = create_stats_request( transaction_id, OFPST_FLOW, length, flags ); assert( buffer != NULL ); flow_stats_request = ( struct ofp_flow_stats_request * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) ); hton_match( &flow_stats_request->match, &m ); flow_stats_request->table_id = table_id; flow_stats_request->pad = 0; flow_stats_request->out_port = htons( out_port ); return buffer; } buffer * create_aggregate_stats_request( const uint32_t transaction_id, const uint16_t flags, const struct ofp_match match, const uint8_t table_id, const uint16_t out_port ) { char match_str[ 1024 ]; uint16_t length; buffer *buffer; struct ofp_match m = match; struct ofp_aggregate_stats_request *aggregate_stats_request; // Because match_to_string() is costly, we check logging_level first. if ( get_logging_level() >= LOG_DEBUG ) { match_to_string( &m, match_str, sizeof( match_str ) ); debug( "Creating an aggregate stats request ( xid = %#x, flags = %#x, match = [%s], table_id = %u, out_port = %u ).", transaction_id, flags, match_str, table_id, out_port ); } length = ( uint16_t ) ( offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_aggregate_stats_request ) ); buffer = create_stats_request( transaction_id, OFPST_AGGREGATE, length, flags ); assert( buffer != NULL ); aggregate_stats_request = ( struct ofp_aggregate_stats_request * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) ); hton_match( &aggregate_stats_request->match, &m ); aggregate_stats_request->table_id = table_id; aggregate_stats_request->pad = 0; aggregate_stats_request->out_port = htons( out_port ); return buffer; } buffer * create_table_stats_request( const uint32_t transaction_id, const uint16_t flags ) { debug( "Creating a table stats request ( xid = %#x, flags = %#x ).", transaction_id, flags ); return create_stats_request( transaction_id, OFPST_TABLE, sizeof( struct ofp_stats_request ), flags ); } buffer * create_port_stats_request( const uint32_t transaction_id, const uint16_t flags, const uint16_t port_no ) { uint16_t length; buffer *buffer; struct ofp_port_stats_request *port_stats_request; debug( "Creating a port stats request ( xid = %#x, flags = %#x, port_no = %u ).", transaction_id, flags, port_no ); length = ( uint16_t ) ( offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_port_stats_request ) ); buffer = create_stats_request( transaction_id, OFPST_PORT, length, flags ); assert( buffer != NULL ); port_stats_request = ( struct ofp_port_stats_request * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) ); port_stats_request->port_no = htons( port_no ); memset( &port_stats_request->pad, 0, sizeof( port_stats_request->pad ) ); return buffer; } buffer * create_queue_stats_request( const uint32_t transaction_id, const uint16_t flags, const uint16_t port_no, const uint32_t queue_id ) { uint16_t length; buffer *buffer; struct ofp_queue_stats_request *queue_stats_request; debug( "Creating a queue stats request ( xid = %#x, flags = %#x, port_no = %u, queue_id = %u ).", transaction_id, flags, port_no, queue_id ); length = ( uint16_t ) ( offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_queue_stats_request ) ); buffer = create_stats_request( transaction_id, OFPST_QUEUE, length, flags ); assert( buffer != NULL ); queue_stats_request = ( struct ofp_queue_stats_request * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) ); queue_stats_request->port_no = htons( port_no ); memset( &queue_stats_request->pad, 0, sizeof( queue_stats_request->pad ) ); queue_stats_request->queue_id = htonl( queue_id ); return buffer; } buffer * create_vendor_stats_request( const uint32_t transaction_id, const uint16_t flags, const uint32_t vendor, const buffer *body ) { void *b; uint16_t length; uint16_t data_length = 0; uint32_t *v; buffer *buffer; if ( ( body != NULL ) && ( body->length > 0 ) ) { data_length = ( uint16_t ) body->length; } debug( "Creating a vendor stats request ( xid = %#x, flags = %#x, vendor = %#x, data length = %u ).", transaction_id, flags, vendor, data_length ); length = ( uint16_t ) ( offsetof( struct ofp_stats_request, body ) + sizeof( uint32_t ) + data_length ); buffer = create_stats_request( transaction_id, OFPST_VENDOR, length, flags ); assert( buffer != NULL ); v = ( uint32_t * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) ); *v = htonl( vendor ); if ( data_length > 0 ) { b = ( void * ) ( ( char * ) buffer->data + offsetof( struct ofp_stats_request, body ) + sizeof( uint32_t ) ); memcpy( b, body->data, data_length ); } return buffer; } static buffer * create_stats_reply( const uint32_t transaction_id, const uint16_t type, const uint16_t length, const uint16_t flags ) { buffer *buffer; struct ofp_stats_reply *stats_reply; debug( "Creating a stats reply ( xid = %#x, type = %#x, length = %u, flags = %#x ).", transaction_id, type, length, flags ); buffer = create_header( transaction_id, OFPT_STATS_REPLY, length ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; stats_reply->type = htons( type ); stats_reply->flags = htons( flags ); return buffer; } buffer * create_desc_stats_reply( const uint32_t transaction_id, const uint16_t flags, const char mfr_desc[ DESC_STR_LEN ], const char hw_desc[ DESC_STR_LEN ], const char sw_desc[ DESC_STR_LEN ], const char serial_num[ SERIAL_NUM_LEN ], const char dp_desc[ DESC_STR_LEN ] ) { uint16_t length; buffer *buffer; struct ofp_stats_reply *stats_reply; struct ofp_desc_stats *desc_stats; debug( "Creating a description stats reply " "( xid = %#x, flags = %#x, mfr_desc = %s, hw_desc = %s, sw_desc = %s, serial_num = %s, dp_desc = %s ).", transaction_id, flags, mfr_desc, hw_desc, sw_desc, serial_num, dp_desc ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_desc_stats ) ); buffer = create_stats_reply( transaction_id, OFPST_DESC, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; desc_stats = ( struct ofp_desc_stats * ) stats_reply->body; memcpy( desc_stats->mfr_desc, mfr_desc, DESC_STR_LEN ); memcpy( desc_stats->hw_desc, hw_desc, DESC_STR_LEN ); memcpy( desc_stats->sw_desc, sw_desc, DESC_STR_LEN ); memcpy( desc_stats->serial_num, serial_num, DESC_STR_LEN ); memcpy( desc_stats->dp_desc, dp_desc, DESC_STR_LEN ); return buffer; } buffer * create_flow_stats_reply( const uint32_t transaction_id, const uint16_t flags, const list_element *flows_stats_head ) { int n_flows = 0; uint16_t length = 0; buffer *buffer; list_element *f = NULL; list_element *flow = NULL; struct ofp_stats_reply *stats_reply; struct ofp_flow_stats *fs, *flow_stats; debug( "Creating a flow stats reply ( xid = %#x, flags = %#x ).", transaction_id, flags ); if ( flows_stats_head != NULL ) { f = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( f, flows_stats_head, sizeof( list_element ) ); } flow = f; while ( flow != NULL ) { flow_stats = flow->data; length = ( uint16_t ) ( length + flow_stats->length ); n_flows++; flow = flow->next; } debug( "# of flows = %u.", n_flows ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + length ); buffer = create_stats_reply( transaction_id, OFPST_FLOW, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; flow_stats = ( struct ofp_flow_stats * ) stats_reply->body; flow = f; while ( flow != NULL ) { fs = ( struct ofp_flow_stats * ) flow->data; hton_flow_stats( flow_stats, fs ); flow_stats = ( struct ofp_flow_stats * ) ( ( char * ) flow_stats + fs->length ); flow = flow->next; } if ( f != NULL ) { xfree( f ); } return buffer; } buffer * create_aggregate_stats_reply( const uint32_t transaction_id, const uint16_t flags, const uint64_t packet_count, const uint64_t byte_count, const uint32_t flow_count ) { uint16_t length; buffer *buffer; struct ofp_stats_reply *stats_reply; struct ofp_aggregate_stats_reply *aggregate_stats_reply; debug( "Creating an aggregate stats reply " "( xid = %#x, flags = %#x, packet_count = %" PRIu64 ", byte_count = %" PRIu64 ", flow_count = %u ).", transaction_id, flags, packet_count, byte_count, flow_count ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_aggregate_stats_reply ) ); buffer = create_stats_reply( transaction_id, OFPST_AGGREGATE, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; aggregate_stats_reply = ( struct ofp_aggregate_stats_reply * ) stats_reply->body; aggregate_stats_reply->packet_count = htonll( packet_count ); aggregate_stats_reply->byte_count = htonll( byte_count ); aggregate_stats_reply->flow_count = htonl( flow_count ); memset( &aggregate_stats_reply->pad, 0, sizeof( aggregate_stats_reply->pad ) ); return buffer; } buffer * create_table_stats_reply( const uint32_t transaction_id, const uint16_t flags, const list_element *table_stats_head ) { uint16_t length; uint16_t n_tables = 0; buffer *buffer; list_element *t = NULL; list_element *table = NULL; struct ofp_stats_reply *stats_reply; struct ofp_table_stats *ts, *table_stats; debug( "Creating a table stats reply ( xid = %#x, flags = %#x ).", transaction_id, flags ); if ( table_stats_head != NULL ) { t = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( t, table_stats_head, sizeof( list_element ) ); } table = t; while ( table != NULL ) { n_tables++; table = table->next; } debug( "# of tables = %u.", n_tables ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_table_stats ) * n_tables ); buffer = create_stats_reply( transaction_id, OFPST_TABLE, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; table_stats = ( struct ofp_table_stats * ) stats_reply->body; table = t; while ( table != NULL ) { ts = ( struct ofp_table_stats * ) table->data; hton_table_stats( table_stats, ts ); table = table->next; table_stats++; } if ( t != NULL ) { xfree( t ); } return buffer; } buffer * create_port_stats_reply( const uint32_t transaction_id, const uint16_t flags, const list_element *port_stats_head ) { uint16_t length; uint16_t n_ports = 0; buffer *buffer; list_element *p = NULL; list_element *port = NULL; struct ofp_stats_reply *stats_reply; struct ofp_port_stats *ps, *port_stats; debug( "Creating a port stats reply ( xid = %#x, flags = %#x ).", transaction_id, flags ); if ( port_stats_head != NULL ) { p = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( p, port_stats_head, sizeof( list_element ) ); } port = p; while ( port != NULL ) { n_ports++; port = port->next; } debug( "# of ports = %u.", n_ports ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_port_stats ) * n_ports ); buffer = create_stats_reply( transaction_id, OFPST_PORT, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; port_stats = ( struct ofp_port_stats * ) stats_reply->body; port = p; while ( port != NULL ) { ps = ( struct ofp_port_stats * ) port->data; hton_port_stats( port_stats, ps ); port = port->next; port_stats++; } if ( p != NULL ) { xfree( p ); } return buffer; } buffer * create_queue_stats_reply( const uint32_t transaction_id, const uint16_t flags, const list_element *queue_stats_head ) { uint16_t length; uint16_t n_queues = 0; buffer *buffer; list_element *q = NULL; list_element *queue = NULL; struct ofp_stats_reply *stats_reply; struct ofp_queue_stats *qs, *queue_stats; debug( "Creating a queue stats reply ( xid = %#x, flags = %#x ).", transaction_id, flags ); if ( queue_stats_head != NULL ) { q = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( q, queue_stats_head, sizeof( list_element ) ); } queue = q; while ( queue != NULL ) { n_queues++; queue = queue->next; } debug( "# of queues = %u.", n_queues ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_queue_stats ) * n_queues ); buffer = create_stats_reply( transaction_id, OFPST_QUEUE, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; queue_stats = ( struct ofp_queue_stats * ) stats_reply->body; queue = q; while ( queue != NULL ) { qs = ( struct ofp_queue_stats * ) queue->data; hton_queue_stats( queue_stats, qs ); queue = queue->next; queue_stats++; } if ( q != NULL ) { xfree( q ); } return buffer; } buffer * create_vendor_stats_reply( const uint32_t transaction_id, const uint16_t flags, const uint32_t vendor, const buffer *body ) { void *b; uint16_t length; uint16_t data_length = 0; uint32_t *v; buffer *buffer; struct ofp_stats_reply *stats_reply; if ( ( body != NULL ) && ( body->length > 0 ) ) { data_length = ( uint16_t ) body->length; } debug( "Creating a vendor stats reply ( xid = %#x, flags = %#x, vendor = %#x, data length = %u ).", transaction_id, flags, vendor, data_length ); length = ( uint16_t ) ( offsetof( struct ofp_stats_reply, body ) + sizeof( uint32_t ) + data_length ); buffer = create_stats_reply( transaction_id, OFPST_VENDOR, length, flags ); assert( buffer != NULL ); stats_reply = ( struct ofp_stats_reply * ) buffer->data; v = ( uint32_t * ) stats_reply->body; *v = htonl( vendor ); if ( data_length > 0 ) { b = ( void * ) ( ( char * ) v + sizeof( uint32_t ) ); memcpy( b, body->data, data_length ); } return buffer; } buffer * create_barrier_request( const uint32_t transaction_id ) { debug( "Creating a barrier request ( xid = %#x ).", transaction_id ); return create_header( transaction_id, OFPT_BARRIER_REQUEST, sizeof( struct ofp_header ) ); } buffer * create_barrier_reply( const uint32_t transaction_id ) { debug( "Creating a barrier reply ( xid = %#x ).", transaction_id ); return create_header( transaction_id, OFPT_BARRIER_REPLY, sizeof( struct ofp_header ) ); } buffer * create_queue_get_config_request( const uint32_t transaction_id, const uint16_t port ) { buffer *buffer; struct ofp_queue_get_config_request *queue_get_config_request; debug( "Creating a queue get config request ( xid = %#x, port = %u ).", transaction_id, port ); buffer = create_header( transaction_id, OFPT_QUEUE_GET_CONFIG_REQUEST, sizeof( struct ofp_queue_get_config_request ) ); assert( buffer != NULL ); queue_get_config_request = ( struct ofp_queue_get_config_request * ) buffer->data; queue_get_config_request->port = htons( port ); memset( queue_get_config_request->pad, 0, sizeof( queue_get_config_request->pad ) ); return buffer; } buffer * create_queue_get_config_reply( const uint32_t transaction_id, const uint16_t port, const list_element *queues ) { uint16_t length; uint16_t n_queues = 0; uint16_t queues_length = 0; buffer *buffer; list_element *q, *queue; struct ofp_queue_get_config_reply *queue_get_config_reply; struct ofp_packet_queue *pq, *packet_queue; debug( "Creating a queue get config reply ( xid = %#x, port = %u ).", transaction_id, port ); #ifndef UNIT_TESTING assert( queues != NULL ); #endif if ( queues != NULL ) { q = ( list_element * ) xmalloc( sizeof( list_element ) ); memcpy( q, queues, sizeof( list_element ) ); queue = q; while ( queue != NULL ) { packet_queue = ( struct ofp_packet_queue * ) queue->data; queues_length = ( uint16_t ) ( queues_length + packet_queue->len ); n_queues++; queue = queue->next; } } debug( "# of queues = %u.", n_queues ); length = ( uint16_t ) ( offsetof( struct ofp_queue_get_config_reply, queues ) + queues_length ); buffer = create_header( transaction_id, OFPT_QUEUE_GET_CONFIG_REPLY, length ); assert( buffer != NULL ); queue_get_config_reply = ( struct ofp_queue_get_config_reply * ) buffer->data; queue_get_config_reply->port = htons( port ); memset( &queue_get_config_reply->pad, 0, sizeof( queue_get_config_reply->pad ) ); packet_queue = ( struct ofp_packet_queue * ) queue_get_config_reply->queues; if ( n_queues ) { queue = q; while ( queue != NULL ) { pq = ( struct ofp_packet_queue * ) queue->data; hton_packet_queue( packet_queue, pq ); packet_queue = ( struct ofp_packet_queue * ) ( ( char * ) packet_queue + pq->len ); queue = queue->next; } xfree( q ); } return buffer; } uint32_t get_transaction_id( void ) { debug( "Generating a transaction id." ); pthread_mutex_lock( &transaction_id_mutex ); if ( ( transaction_id & 0xffff ) == 0xffff ) { transaction_id = transaction_id & 0xffff0000; } else { transaction_id++; } pthread_mutex_unlock( &transaction_id_mutex ); debug( "Transaction id = %#x.", transaction_id ); return transaction_id; } uint64_t get_cookie( void ) { debug( "Generating a cookie." ); pthread_mutex_lock( &cookie_mutex ); if ( ( cookie & 0x0000ffffffffffffULL ) == 0x0000ffffffffffffULL ) { cookie = cookie & 0xffff000000000000ULL; } else { cookie++; } pthread_mutex_unlock( &cookie_mutex ); debug( "Cookie = %#" PRIx64 ".", cookie ); return cookie; } openflow_actions * create_actions() { openflow_actions *actions; debug( "Creating an empty actions list." ); actions = ( openflow_actions * ) xmalloc( sizeof( openflow_actions ) ); if ( create_list( &actions->list ) == false ) { assert( 0 ); } actions->n_actions = 0; return actions; } bool delete_actions( openflow_actions *actions ) { list_element *element; debug( "Deleting an actions list." ); assert( actions != NULL ); debug( "# of actions = %d.", actions->n_actions ); element = actions->list; while ( element != NULL ) { xfree( element->data ); element = element->next; } delete_list( actions->list ); xfree( actions ); return true; } bool append_action_output( openflow_actions *actions, const uint16_t port, const uint16_t max_len ) { bool ret; struct ofp_action_output *action_output; debug( "Appending an output action ( port = %u, max_len = %u ).", port, max_len ); assert( actions != NULL ); action_output = ( struct ofp_action_output * ) xcalloc( 1, sizeof( struct ofp_action_output ) ); action_output->type = OFPAT_OUTPUT; action_output->len = sizeof( struct ofp_action_output ); action_output->port = port; action_output->max_len = max_len; ret = append_to_tail( &actions->list, ( void * ) action_output ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_set_vlan_vid( openflow_actions *actions, const uint16_t vlan_vid ) { bool ret; struct ofp_action_vlan_vid *action_vlan_vid; debug( "Appending a set vlan action ( vlan_vid = %#x ).", vlan_vid ); assert( actions != NULL ); assert( ( vlan_vid & ~VLAN_VID_MASK ) == 0 ); action_vlan_vid = ( struct ofp_action_vlan_vid * ) xcalloc( 1, sizeof( struct ofp_action_vlan_vid ) ); action_vlan_vid->type = OFPAT_SET_VLAN_VID; action_vlan_vid->len = sizeof( struct ofp_action_vlan_vid ); action_vlan_vid->vlan_vid = vlan_vid; ret = append_to_tail( &actions->list, ( void * ) action_vlan_vid ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_set_vlan_pcp( openflow_actions *actions, const uint8_t vlan_pcp ) { bool ret; struct ofp_action_vlan_pcp *action_vlan_pcp; debug( "Appending a set vlan pcp action ( vlan_pcp = %#x ).", vlan_pcp ); assert( actions != NULL ); assert( ( vlan_pcp & ~VLAN_PCP_MASK ) == 0 ); action_vlan_pcp = ( struct ofp_action_vlan_pcp * ) xcalloc( 1, sizeof( struct ofp_action_vlan_pcp ) ); action_vlan_pcp->type = OFPAT_SET_VLAN_PCP; action_vlan_pcp->len = sizeof( struct ofp_action_vlan_pcp ); action_vlan_pcp->vlan_pcp = vlan_pcp; ret = append_to_tail( &actions->list, ( void * ) action_vlan_pcp ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_strip_vlan( openflow_actions *actions ) { bool ret; struct ofp_action_header *action_strip_vlan; debug( "Appending a strip vlan action." ); assert( actions != NULL ); action_strip_vlan = ( struct ofp_action_header * ) xcalloc( 1, sizeof( struct ofp_action_header ) ); action_strip_vlan->type = OFPAT_STRIP_VLAN; action_strip_vlan->len = sizeof( struct ofp_action_header ); ret = append_to_tail( &actions->list, ( void * ) action_strip_vlan ); if ( ret ) { actions->n_actions++; } return ret; } static bool append_action_set_dl_addr( openflow_actions *actions, const uint16_t type, const uint8_t hw_addr[ OFP_ETH_ALEN ] ) { bool ret; struct ofp_action_dl_addr *action_dl_addr; debug( "Appending a set dl_src/dl_dst action ( type = %#x, hw_addr = %02x:%02x:%02x:%02x:%02x:%02x ).", type, hw_addr[ 0 ], hw_addr[ 1 ], hw_addr[ 2 ], hw_addr[ 3 ], hw_addr[ 4 ], hw_addr[ 5 ] ); assert( actions != NULL ); action_dl_addr = ( struct ofp_action_dl_addr * ) xcalloc( 1, sizeof( struct ofp_action_dl_addr ) ); action_dl_addr->type = type; action_dl_addr->len = sizeof( struct ofp_action_dl_addr ); memcpy( action_dl_addr->dl_addr, hw_addr, OFP_ETH_ALEN ); ret = append_to_tail( &actions->list, ( void * ) action_dl_addr ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_set_dl_src( openflow_actions *actions, const uint8_t hw_addr[ OFP_ETH_ALEN ] ) { debug( "Appending a set dl_src action ( hw_addr = %02x:%02x:%02x:%02x:%02x:%02x ).", hw_addr[ 0 ], hw_addr[ 1 ], hw_addr[ 2 ], hw_addr[ 3 ], hw_addr[ 4 ], hw_addr[ 5 ] ); assert( actions != NULL ); return append_action_set_dl_addr( actions, OFPAT_SET_DL_SRC, hw_addr ); } bool append_action_set_dl_dst( openflow_actions *actions, const uint8_t hw_addr[ OFP_ETH_ALEN ] ) { debug( "Appending a set dl_dst action ( hw_addr = %02x:%02x:%02x:%02x:%02x:%02x ).", hw_addr[ 0 ], hw_addr[ 1 ], hw_addr[ 2 ], hw_addr[ 3 ], hw_addr[ 4 ], hw_addr[ 5 ] ); assert( actions != NULL ); return append_action_set_dl_addr( actions, OFPAT_SET_DL_DST, hw_addr ); } static bool append_action_set_nw_addr( openflow_actions *actions, const uint16_t type, const uint32_t nw_addr ) { bool ret; char addr_str[ 16 ]; struct in_addr addr; struct ofp_action_nw_addr *action_nw_addr; addr.s_addr = htonl( nw_addr ); memset( addr_str, '\0', sizeof( addr_str ) ); inet_ntop( AF_INET, &addr, addr_str, sizeof( addr_str ) ); debug( "Appending a set nw_src/nw_dst action ( type = %#x, nw_addr = %s ).", type, addr_str ); assert( actions != NULL ); action_nw_addr = ( struct ofp_action_nw_addr * ) xcalloc( 1, sizeof( struct ofp_action_nw_addr ) ); action_nw_addr->type = type; action_nw_addr->len = sizeof( struct ofp_action_nw_addr ); action_nw_addr->nw_addr = nw_addr; ret = append_to_tail( &actions->list, ( void * ) action_nw_addr ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_set_nw_src( openflow_actions *actions, const uint32_t nw_addr ) { char addr_str[ 16 ]; struct in_addr addr; addr.s_addr = htonl( nw_addr ); memset( addr_str, '\0', sizeof( addr_str ) ); inet_ntop( AF_INET, &addr, addr_str, sizeof( addr_str ) ); debug( "Appending a set nw_src action ( nw_addr = %s ).", addr_str ); assert( actions != NULL ); return append_action_set_nw_addr( actions, OFPAT_SET_NW_SRC, nw_addr ); } bool append_action_set_nw_dst( openflow_actions *actions, const uint32_t nw_addr ) { char addr_str[ 16 ]; struct in_addr addr; addr.s_addr = htonl( nw_addr ); memset( addr_str, '\0', sizeof( addr_str ) ); inet_ntop( AF_INET, &addr, addr_str, sizeof( addr_str ) ); debug( "Appending a set nw_dst action ( nw_addr = %s ).", addr_str ); assert( actions != NULL ); return append_action_set_nw_addr( actions, OFPAT_SET_NW_DST, nw_addr ); } bool append_action_set_nw_tos( openflow_actions *actions, const uint8_t nw_tos ) { bool ret; struct ofp_action_nw_tos *action_nw_tos; debug( "Appending a set nw_tos action ( nw_tos = %#x ).", nw_tos ); assert( actions != NULL ); assert( ( nw_tos & ~NW_TOS_MASK ) == 0 ); action_nw_tos = ( struct ofp_action_nw_tos * ) xcalloc( 1, sizeof( struct ofp_action_nw_tos ) ); action_nw_tos->type = OFPAT_SET_NW_TOS; action_nw_tos->len = sizeof( struct ofp_action_nw_tos ); action_nw_tos->nw_tos = nw_tos; ret = append_to_tail( &actions->list, ( void * ) action_nw_tos ); if ( ret ) { actions->n_actions++; } return ret; } static bool append_action_set_tp_port( openflow_actions *actions, const uint16_t type, const uint16_t tp_port ) { bool ret; struct ofp_action_tp_port *action_tp_port; debug( "Appending a set tp_src/tp_dst action ( type = %#x, tp_port = %u ).", type, tp_port ); assert( actions != NULL ); action_tp_port = ( struct ofp_action_tp_port * ) xcalloc( 1, sizeof( struct ofp_action_tp_port ) ); action_tp_port->type = type; action_tp_port->len = sizeof( struct ofp_action_tp_port ); action_tp_port->tp_port = tp_port; ret = append_to_tail( &actions->list, ( void * ) action_tp_port ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_set_tp_src( openflow_actions *actions, const uint16_t tp_port ) { debug( "Appending a set tp_src action ( tp_port = %u ).", tp_port ); assert( actions != NULL ); return append_action_set_tp_port( actions, OFPAT_SET_TP_SRC, tp_port ); } bool append_action_set_tp_dst( openflow_actions *actions, const uint16_t tp_port ) { debug( "Appending a set tp_dst action ( tp_port = %u ).", tp_port ); assert( actions != NULL ); return append_action_set_tp_port( actions, OFPAT_SET_TP_DST, tp_port ); } bool append_action_enqueue( openflow_actions *actions, const uint16_t port, const uint32_t queue_id ) { bool ret; struct ofp_action_enqueue *action_enqueue; debug( "Appending an enqueue action ( port = %u, queue_id = %u ).", port, queue_id ); assert( actions != NULL ); action_enqueue = ( struct ofp_action_enqueue * ) xcalloc( 1, sizeof( struct ofp_action_enqueue ) ); action_enqueue->type = OFPAT_ENQUEUE; action_enqueue->len = sizeof( struct ofp_action_enqueue ); action_enqueue->port = port; action_enqueue->queue_id = queue_id; ret = append_to_tail( &actions->list, ( void * ) action_enqueue ); if ( ret ) { actions->n_actions++; } return ret; } bool append_action_vendor( openflow_actions *actions, const uint32_t vendor, const buffer *body ) { bool ret; uint16_t body_length = 0; struct ofp_action_vendor_header *action_vendor; if ( ( body != NULL ) && ( body->length > 0 ) ) { body_length = ( uint16_t ) body->length; } debug( "Appending a vendor action ( vendor = %#" PRIx64 ", body length = %u ).", vendor, body_length ); assert( actions != NULL ); action_vendor = ( struct ofp_action_vendor_header * ) xcalloc( 1, sizeof( struct ofp_action_vendor_header ) + body_length ); action_vendor->type = OFPAT_VENDOR; action_vendor->len = ( uint16_t ) ( sizeof( struct ofp_action_vendor_header ) + body_length ); action_vendor->vendor = vendor; ret = append_to_tail( &actions->list, ( void * ) action_vendor ); if ( ret ) { actions->n_actions++; } return ret; } static int validate_header( const buffer *message, const uint8_t type, const uint16_t min_length, const uint16_t max_length ) { struct ofp_header *header; assert( message != NULL ); if ( message->length < sizeof( struct ofp_header ) ) { return ERROR_TOO_SHORT_MESSAGE; } header = ( struct ofp_header * ) message->data; if ( header->version != OFP_VERSION ) { return ERROR_UNSUPPORTED_VERSION; } if ( header->type > OFPT_QUEUE_GET_CONFIG_REPLY ) { return ERROR_UNDEFINED_TYPE; } if ( header->type != type ) { return ERROR_INVALID_TYPE; } if ( ntohs( header->length ) > max_length ) { return ERROR_TOO_LONG_MESSAGE; } else if ( ntohs( header->length ) < min_length ) { return ERROR_TOO_SHORT_MESSAGE; } if ( ntohs( header->length ) < message->length ) { return ERROR_TOO_LONG_MESSAGE; } else if ( ntohs( header->length ) > message->length ) { return ERROR_TOO_SHORT_MESSAGE; } if ( message->length > max_length ) { return ERROR_TOO_LONG_MESSAGE; } return 0; } int validate_hello( const buffer *message ) { assert( message != NULL ); return validate_header( message, OFPT_HELLO, sizeof( struct ofp_header ), sizeof( struct ofp_header ) ); } int validate_error( const buffer *message ) { int ret; assert( message != NULL ); ret = validate_header( message, OFPT_ERROR, sizeof( struct ofp_error_msg ), UINT16_MAX ); if ( ret < 0 ) { return ret; } return 0; } int validate_echo_request( const buffer *message ) { int ret; struct ofp_header *header; assert( message != NULL ); ret = validate_header( message, OFPT_ECHO_REQUEST, sizeof( struct ofp_header ), UINT16_MAX ); if ( ret < 0 ) { return ret; } header = ( struct ofp_header * ) message->data; if ( message->length != ntohs( header->length ) ) { return ERROR_INVALID_LENGTH; } return 0; } int validate_echo_reply( const buffer *message ) { int ret; struct ofp_header *header; assert( message != NULL ); ret = validate_header( message, OFPT_ECHO_REPLY, sizeof( struct ofp_header ), UINT16_MAX ); if ( ret < 0 ) { return ret; } header = ( struct ofp_header * ) message->data; if ( message->length != ntohs( header->length ) ) { return ERROR_INVALID_LENGTH; } return 0; } int validate_vendor( const buffer *message ) { int ret; struct ofp_vendor_header *vendor_header; assert( message != NULL ); ret = validate_header( message, OFPT_VENDOR, sizeof( struct ofp_vendor_header ), UINT16_MAX ); if ( ret < 0 ) { return ret; } vendor_header = ( struct ofp_vendor_header * ) message->data; if ( message->length != ntohs( vendor_header->header.length ) ) { return ERROR_INVALID_LENGTH; } return 0; } int validate_features_request( const buffer *message ) { assert( message != NULL ); return validate_header( message, OFPT_FEATURES_REQUEST, sizeof( struct ofp_header ), sizeof( struct ofp_header ) ); } static int validate_phy_port_no( const uint16_t port_no ) { if ( ( port_no == 0 ) || ( ( port_no > OFPP_MAX ) && ( port_no < OFPP_IN_PORT ) ) ) { return ERROR_INVALID_PORT_NO; } return 0; } static int validate_phy_port( struct ofp_phy_port *port ) { int ret; struct ofp_phy_port port_h; assert( port != NULL ); ntoh_phy_port( &port_h, port ); ret = validate_phy_port_no( port_h.port_no ); if ( ret < 0 ) { return ret; } if ( ( port_h.config & ( uint32_t ) ~PORT_CONFIG ) != 0 ) { return ERROR_INVALID_PORT_CONFIG; } if ( ( port_h.state & ( uint32_t ) ~PORT_STATE ) != 0 ) { return ERROR_INVALID_PORT_STATE; } if ( ( port_h.curr & ( uint32_t ) ~PORT_FEATURES ) != 0 || ( port_h.advertised & ( uint32_t ) ~PORT_FEATURES ) != 0 || ( port_h.supported & ( uint32_t ) ~PORT_FEATURES ) != 0 || ( port_h.peer & ( uint32_t ) ~PORT_FEATURES ) != 0 ) { return ERROR_INVALID_PORT_FEATURES; } return 0; } static int validate_phy_ports( struct ofp_phy_port *ports, const int n_ports ) { int i; int ret; struct ofp_phy_port *port; assert( ports != NULL ); assert( n_ports ); port = ports; for ( i = 0; i < n_ports; i++ ) { ret = validate_phy_port( port ); if ( ret < 0 ) { return ret; } port++; } return 0; } int validate_features_reply( const buffer *message ) { void *p; int ret; int n_ports; uint16_t port_length; struct ofp_switch_features *switch_features; assert( message != NULL ); ret = validate_header( message, OFPT_FEATURES_REPLY, sizeof( struct ofp_switch_features ), UINT16_MAX ); if ( ret < 0 ) { return ret; } switch_features = ( struct ofp_switch_features * ) message->data; // switch_features->datapath_id // switch_features->n_buffers if ( switch_features->n_tables == 0 ) { return ERROR_NO_TABLE_AVAILABLE; } port_length = ( uint16_t ) ( ntohs( switch_features->header.length ) - sizeof( struct ofp_switch_features ) ); if ( port_length % sizeof( struct ofp_phy_port ) != 0 ) { return ERROR_INVALID_LENGTH; } if ( port_length > 0 ) { p = ( void * ) ( ( char * ) message->data + offsetof( struct ofp_switch_features, ports ) ); n_ports = port_length / sizeof( struct ofp_phy_port ); ret = validate_phy_ports( p, n_ports ); if ( ret < 0 ) { return ret; } } return 0; } int validate_get_config_request( const buffer *message ) { assert( message != NULL ); return validate_header( message, OFPT_GET_CONFIG_REQUEST, sizeof( struct ofp_header ), sizeof( struct ofp_header ) ); } static int validate_switch_config( const buffer *message, const uint8_t type ) { int ret; struct ofp_switch_config *switch_config; assert( message != NULL ); assert( ( type == OFPT_GET_CONFIG_REPLY ) || ( type == OFPT_SET_CONFIG ) ); ret = validate_header( message, type, sizeof( struct ofp_switch_config ), sizeof( struct ofp_switch_config ) ); if ( ret < 0 ) { return ret; } switch_config = ( struct ofp_switch_config * ) message->data; if ( ntohs( switch_config->flags ) > OFPC_FRAG_MASK ) { return ERROR_INVALID_SWITCH_CONFIG; } // switch_config->miss_send_len return 0; } int validate_get_config_reply( const buffer *message ) { assert( message != NULL ); return validate_switch_config( message, OFPT_GET_CONFIG_REPLY ); } int validate_set_config( const buffer *message ) { assert( message != NULL ); return validate_switch_config( message, OFPT_SET_CONFIG ); } int validate_packet_in( const buffer *message ) { int ret; uint16_t data_length; struct ofp_packet_in *packet_in; assert( message != NULL ); ret = validate_header( message, OFPT_PACKET_IN, offsetof( struct ofp_packet_in, data ), UINT16_MAX ); if ( ret < 0 ) { return ret; } packet_in = ( struct ofp_packet_in * ) message->data; // packet_in->buffer_id // packet_in->total_len // packet_in->in_port ret = validate_phy_port_no( ntohs( packet_in->in_port ) ); if ( ret < 0 ) { return ret; } if ( packet_in->reason > OFPR_ACTION ) { return ERROR_INVALID_PACKET_IN_REASON; } data_length = ( uint16_t ) ( ntohs( packet_in->header.length ) - offsetof( struct ofp_packet_in, data ) ); if ( data_length > 0 ) { // FIXME: it may be better to check if this is a valid Ethernet frame or not. } return 0; } static int validate_wildcards( const uint32_t wildcards ) { if ( ( wildcards & ( uint32_t ) ~OFPFW_ALL ) != 0 ) { return ERROR_INVALID_WILDCARDS; } return 0; } static int validate_vlan_vid( const uint16_t vid ) { if ( ( vid != UINT16_MAX ) && ( ( vid & ~VLAN_VID_MASK ) != 0 ) ) { return ERROR_INVALID_VLAN_VID; } return 0; } static int validate_vlan_pcp( const uint8_t pcp ) { if ( ( pcp & ~VLAN_PCP_MASK ) != 0 ) { return ERROR_INVALID_VLAN_PCP; } return 0; } static int validate_nw_tos( const uint8_t tos ) { if ( ( tos & ~NW_TOS_MASK ) != 0 ) { return ERROR_INVALID_NW_TOS; } return 0; } static int validate_match( const struct ofp_match match ) { int ret; ret = validate_wildcards( match.wildcards ); if ( ret < 0 ) { return ret; } ret = validate_vlan_vid( match.dl_vlan ); if ( ret < 0 ) { return ret; } ret = validate_vlan_pcp( match.dl_vlan_pcp ); if ( ret < 0 ) { return ret; } ret = validate_nw_tos( match.nw_tos ); if ( ret < 0 ) { return ret; } return 0; } int validate_flow_removed( const buffer *message ) { int ret; struct ofp_match match; struct ofp_flow_removed *flow_removed; assert( message != NULL ); ret = validate_header( message, OFPT_FLOW_REMOVED, sizeof( struct ofp_flow_removed ), sizeof( struct ofp_flow_removed ) ); if ( ret < 0 ) { return ret; } flow_removed = ( struct ofp_flow_removed * ) message->data; ntoh_match( &match, &flow_removed->match ); ret = validate_match( match ); if ( ret < 0 ) { return ret; } // flow_removed->cookie if ( ( ( match.wildcards & OFPFW_ALL ) == 0 ) && ( ntohs( flow_removed->priority ) != UINT16_MAX ) ) { return ERROR_INVALID_FLOW_PRIORITY; } if ( flow_removed->reason > OFPRR_DELETE ) { return ERROR_INVALID_FLOW_REMOVED_REASON; } // flow_removed->duration_sec // flow_removed->duration_nsec // flow_removed->idle_timeout // flow_removed->packet_count // flow_removed->byte_count return 0; } int validate_port_status( const buffer *message ) { int ret; struct ofp_port_status *port_status; assert( message != NULL ); ret = validate_header( message, OFPT_PORT_STATUS, sizeof( struct ofp_port_status ), sizeof( struct ofp_port_status ) ); if ( ret < 0 ) { return ret; } port_status = ( struct ofp_port_status * ) message->data; if ( port_status->reason > OFPPR_MODIFY ) { return ERROR_INVALID_PORT_STATUS_REASON; } ret = validate_phy_port( &port_status->desc ); if ( ret < 0 ) { return ret; } return 0; } int validate_packet_out( const buffer *message ) { int ret; uint16_t data_length; struct ofp_packet_out *packet_out; assert( message != NULL ); ret = validate_header( message, OFPT_PACKET_OUT, offsetof( struct ofp_packet_out, actions ), UINT16_MAX ); if ( ret < 0 ) { return ret; } packet_out = ( struct ofp_packet_out * ) message->data; ret = validate_phy_port_no( ntohs( packet_out->in_port ) ); if ( ret < 0 ) { return ret; } if ( ntohs( packet_out->actions_len ) > 0 ) { ret = validate_actions( packet_out->actions, ntohs( packet_out->actions_len ) ); if ( ret < 0 ) { return ret; } } data_length = ( uint16_t ) ( ntohs( packet_out->header.length ) - offsetof( struct ofp_packet_out, actions ) - ntohs( packet_out->actions_len ) ); if ( data_length > 0 ) { // FIXME: it may be better to check if this is a valid Ethernet frame or not. } return 0; } int validate_flow_mod( const buffer *message ) { int ret; uint16_t actions_length; struct ofp_match match; struct ofp_flow_mod *flow_mod; assert( message != NULL ); ret = validate_header( message, OFPT_FLOW_MOD, offsetof( struct ofp_flow_mod, actions ), UINT16_MAX ); if ( ret < 0 ) { return ret; } flow_mod = ( struct ofp_flow_mod * ) message->data; ntoh_match( &match, &flow_mod->match ); ret = validate_match( match ); if ( ret < 0 ) { return ret; } // flow_mod->cookie if ( ntohs( flow_mod->command ) > OFPFC_DELETE_STRICT ) { return ERROR_UNDEFINED_FLOW_MOD_COMMAND; } // flow_mod->idle_timeout // flow_mod->hard_timeout if ( ( ( match.wildcards & OFPFW_ALL ) == 0 ) && ( ntohs( flow_mod->priority ) != UINT16_MAX ) ) { return ERROR_INVALID_FLOW_PRIORITY; } // flow_mod->buffer_id if ( ( ntohs( flow_mod->command ) == OFPFC_DELETE ) || ( ntohs( flow_mod->command ) == OFPFC_DELETE_STRICT ) ) { if ( ntohs( flow_mod->out_port ) != OFPP_NONE ) { ret = validate_phy_port_no( ntohs( flow_mod->out_port ) ); if ( ret < 0 ) { return ret; } } } if ( ( ntohs( flow_mod->flags ) & ~FLOW_MOD_FLAGS ) != 0 ) { return ERROR_INVALID_FLOW_MOD_FLAGS; } actions_length = ( uint16_t ) ( ntohs( flow_mod->header.length ) - offsetof( struct ofp_flow_mod, actions ) ); if ( actions_length > 0 ) { ret = validate_actions( flow_mod->actions, actions_length ); if ( ret < 0 ) { return ret; } } return 0; } int validate_port_mod( const buffer *message ) { int ret; struct ofp_port_mod *port_mod; assert( message != NULL ); ret = validate_header( message, OFPT_PORT_MOD, sizeof( struct ofp_port_mod ), sizeof( struct ofp_port_mod ) ); if ( ret < 0 ) { return ret; } port_mod = ( struct ofp_port_mod * ) message->data; ret = validate_phy_port_no( ntohs( port_mod->port_no ) ); if ( ret < 0 ) { return ret; } if ( ( ntohs( port_mod->port_no ) > OFPP_MAX ) && ( ntohs( port_mod->port_no ) != OFPP_LOCAL ) ) { return ERROR_INVALID_PORT_NO; } // port_mod->hw_addr if ( ( ntohl( port_mod->config ) & ( uint32_t ) ~PORT_CONFIG ) != 0 ) { return ERROR_INVALID_PORT_CONFIG; } if ( ( ntohl( port_mod->mask ) & ( uint32_t ) ~PORT_CONFIG ) != 0 ) { return ERROR_INVALID_PORT_MASK; } if ( ( ntohl( port_mod->advertise ) & ( uint32_t ) ~PORT_CONFIG ) != 0 ) { return ERROR_INVALID_PORT_FEATURES; } return 0; } int validate_desc_stats_request( const buffer *message ) { int ret; struct ofp_stats_request *stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, sizeof( struct ofp_stats_request ), sizeof( struct ofp_stats_request ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_DESC ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } return 0; } int validate_flow_stats_request( const buffer *message ) { int ret; struct ofp_match match; struct ofp_stats_request *stats_request; struct ofp_flow_stats_request *flow_stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_flow_stats_request ), offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_flow_stats_request ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_FLOW ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } flow_stats_request = ( struct ofp_flow_stats_request * ) stats_request->body; ntoh_match( &match, &flow_stats_request->match ); ret = validate_match( match ); if ( ret < 0 ) { return ret; } // flow_stats_request->table_id ret = validate_phy_port_no( ntohs( flow_stats_request->out_port ) ); if ( ret < 0 ) { return ret; } return 0; } int validate_aggregate_stats_request( const buffer *message ) { int ret; struct ofp_match match; struct ofp_stats_request *stats_request; struct ofp_aggregate_stats_request *aggregate_stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_aggregate_stats_request ), offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_aggregate_stats_request ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_AGGREGATE ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } aggregate_stats_request = ( struct ofp_aggregate_stats_request * ) stats_request->body; ntoh_match( &match, &aggregate_stats_request->match ); ret = validate_match( match ); if ( ret < 0 ) { return ret; } // aggregate_stats_request->table_id ret = validate_phy_port_no( ntohs( aggregate_stats_request->out_port ) ); if ( ret < 0 ) { return ret; } return 0; } int validate_table_stats_request( const buffer *message ) { int ret; struct ofp_stats_request *stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ), offsetof( struct ofp_stats_request, body ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_TABLE ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } return 0; } int validate_port_stats_request( const buffer *message ) { int ret; struct ofp_stats_request *stats_request; struct ofp_port_stats_request *port_stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_port_stats_request ), offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_port_stats_request ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_PORT ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } port_stats_request = ( struct ofp_port_stats_request * ) stats_request->body; ret = validate_phy_port_no( ntohs( port_stats_request->port_no ) ); if ( ret < 0 ) { return ret; } if ( ntohs( port_stats_request->port_no ) > OFPP_MAX && ntohs( port_stats_request->port_no ) != OFPP_NONE && ntohs( port_stats_request->port_no ) != OFPP_LOCAL ) { return ERROR_INVALID_PORT_NO; } return 0; } int validate_queue_stats_request( const buffer *message ) { int ret; struct ofp_stats_request *stats_request; struct ofp_queue_stats_request *queue_stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_queue_stats_request ), offsetof( struct ofp_stats_request, body ) + sizeof( struct ofp_queue_stats_request ) ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_QUEUE ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } queue_stats_request = ( struct ofp_queue_stats_request * ) stats_request->body; ret = validate_phy_port_no( ntohs( queue_stats_request->port_no ) ); if ( ret < 0 ) { return ret; } // queue_stats_request->queue_id return 0; } int validate_vendor_stats_request( const buffer *message ) { int ret; struct ofp_stats_request *stats_request; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REQUEST, offsetof( struct ofp_stats_request, body ) + sizeof( uint32_t ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_request = ( struct ofp_stats_request * ) message->data; if ( ntohs( stats_request->type ) != OFPST_VENDOR ) { return ERROR_INVALID_STATS_TYPE; } if ( ntohs( stats_request->flags ) != 0 ) { return ERROR_INVALID_STATS_REQUEST_FLAGS; } // vendor_id return 0; } int validate_stats_request( const buffer *message ) { struct ofp_stats_request *request; assert( message != NULL ); request = ( struct ofp_stats_request * ) message->data; // TODO: if ( request->header.type != OFPT_STATS_REQUEST ) { ... } switch ( ntohs( request->type ) ) { case OFPST_DESC: return validate_desc_stats_request( message ); case OFPST_FLOW: return validate_flow_stats_request( message ); case OFPST_AGGREGATE: return validate_aggregate_stats_request( message ); case OFPST_TABLE: return validate_table_stats_request( message ); case OFPST_PORT: return validate_port_stats_request( message ); case OFPST_QUEUE: return validate_queue_stats_request( message ); case OFPST_VENDOR: return validate_vendor_stats_request( message ); default: break; } return ERROR_UNSUPPORTED_STATS_TYPE; } int validate_desc_stats_reply( const buffer *message ) { int ret; struct ofp_stats_reply *stats_reply; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_desc_stats ), offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_desc_stats ) ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ntohs( stats_reply->flags ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } return 0; } int validate_flow_stats_reply( const buffer *message ) { int ret; uint16_t offset; uint16_t flow_length; uint16_t actions_length; struct ofp_stats_reply *stats_reply; struct ofp_flow_stats *flow_stats; struct ofp_action_header *actions_head; struct ofp_match match; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ( ntohs( stats_reply->flags ) & ~OFPSF_REPLY_MORE ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } flow_length = ( uint16_t ) ( ntohs( stats_reply->header.length ) - offsetof( struct ofp_stats_reply, body ) ); offset = offsetof( struct ofp_stats_reply, body ); flow_stats = ( struct ofp_flow_stats * ) ( ( char * ) message->data + offset ); while ( flow_length > 0 ) { // flow_stats->length // flow_stats->table_id ntoh_match( &match, &flow_stats->match ); ret = validate_match( match ); if ( ret < 0 ) { return ret; } // flow_stats->duration_sec // flow_stats->duration_nsec if ( ( ( match.wildcards & OFPFW_ALL ) == 0 ) && ( ntohs( flow_stats->priority ) < UINT16_MAX ) ) { return ERROR_INVALID_FLOW_PRIORITY; } // flow_stats->idle_timeout // flow_stats->hard_timeout // flow_stats->cookie // flow_stats->packet_count // flow_stats->byte_count actions_length = ( uint16_t ) ( ntohs( flow_stats->length ) - offsetof( struct ofp_flow_stats, actions ) ); if ( actions_length > 0 ) { actions_head = ( struct ofp_action_header * ) ( ( char * ) flow_stats + offsetof( struct ofp_flow_stats, actions ) ); ret = validate_actions( actions_head, actions_length ); if ( ret < 0 ) { return ret; } } flow_length = ( uint16_t ) ( flow_length - ntohs( flow_stats->length ) ); flow_stats = ( struct ofp_flow_stats * ) ( ( char * ) flow_stats + ntohs( flow_stats->length ) ); } return 0; } int validate_aggregate_stats_reply( const buffer *message ) { int ret; struct ofp_stats_reply *stats_reply; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_aggregate_stats_reply ), offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_aggregate_stats_reply ) ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ntohs( stats_reply->flags ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } // uint16_t offset = offsetof( struct ofp_stats_reply, body ); // struct ofp_aggregate_stats_reply *aggregate_stats = ( struct ofp_aggregate_stats_reply * ) ( ( char * ) message->data + offset ); // aggregate_stats->packet_count // aggregate_stats->byte_count // aggregate_stats->flow_count return 0; } int validate_table_stats_reply( const buffer *message ) { int i; int ret; uint16_t tables_length; uint16_t n_tables; uint16_t offset; struct ofp_stats_reply *stats_reply; struct ofp_table_stats *table_stats; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_table_stats ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ( ntohs( stats_reply->flags ) & ~OFPSF_REPLY_MORE ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } tables_length = ( uint16_t ) ( ntohs( stats_reply->header.length ) - offsetof( struct ofp_stats_reply, body ) ); if ( tables_length % sizeof( struct ofp_table_stats ) != 0 ) { return ERROR_INVALID_LENGTH; } offset = offsetof( struct ofp_stats_reply, body ); table_stats = ( struct ofp_table_stats * ) ( ( char * ) message->data + offset ); n_tables = tables_length / sizeof( struct ofp_table_stats ); for ( i = 0; i < n_tables; i++ ) { // table_stats->table_id ret = validate_wildcards( ntohl( table_stats->wildcards ) ); if ( ret < 0 ) { return ret; } // table_stats->max_entries // table_stats->active_count // table_stats->lookup_count // table_stats->matched_count table_stats++; } return 0; } int validate_port_stats_reply( const buffer *message ) { int i; int ret; uint16_t ports_length; uint16_t n_ports; uint16_t offset; struct ofp_stats_reply *stats_reply; struct ofp_port_stats *port_stats; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ) + sizeof( struct ofp_port_stats ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ( ntohs( stats_reply->flags ) & ~OFPSF_REPLY_MORE ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } ports_length = ( uint16_t ) ( ntohs( stats_reply->header.length ) - offsetof( struct ofp_stats_reply, body ) ); if ( ports_length % sizeof( struct ofp_port_stats ) != 0 ) { return ERROR_INVALID_LENGTH; } offset = offsetof( struct ofp_stats_reply, body ); port_stats = ( struct ofp_port_stats * ) ( ( char * ) message->data + offset ); n_ports = ports_length / sizeof( struct ofp_port_stats ); for ( i = 0; i < n_ports; i++ ) { ret = validate_phy_port_no( ntohs( port_stats->port_no ) ); if ( ret < 0 ) { return ret; } // port_stats->rx_packets // port_stats->tx_packets // port_stats->rx_bytes // port_stats->tx_bytes // port_stats->rx_dropped // port_stats->tx_dropped // port_stats->rx_errors // port_stats->tx_errors // port_stats->rx_frame_err // port_stats->rx_over_err // port_stats->rx_crc_err // port_stats->collisions port_stats++; } return 0; } int validate_queue_stats_reply( const buffer *message ) { int i; int ret; uint16_t queues_length; uint16_t n_queues; uint16_t offset; struct ofp_stats_reply *stats_reply; struct ofp_queue_stats *queue_stats; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ( ntohs( stats_reply->flags ) & ~OFPSF_REPLY_MORE ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } queues_length = ( uint16_t ) ( ntohs( stats_reply->header.length ) - offsetof( struct ofp_stats_reply, body ) ); if ( queues_length % sizeof( struct ofp_queue_stats ) != 0 ) { return ERROR_INVALID_LENGTH; } offset = offsetof( struct ofp_stats_reply, body ); queue_stats = ( struct ofp_queue_stats * ) ( ( char * ) message->data + offset ); n_queues = queues_length / sizeof( struct ofp_queue_stats ); for ( i = 0; i < n_queues; i++ ) { ret = validate_phy_port_no( ntohs( queue_stats->port_no ) ); if ( ret < 0 ) { return ret; } // queue_stats->queue_id // queue_stats->tx_bytes // queue_stats->tx_packets // queue_stats->tx_errors queue_stats++; } return 0; } int validate_vendor_stats_reply( const buffer *message ) { void *body; int ret; uint16_t body_length; uint16_t offset; struct ofp_stats_reply *stats_reply; assert( message != NULL ); ret = validate_header( message, OFPT_STATS_REPLY, offsetof( struct ofp_stats_reply, body ) + sizeof( uint32_t ), UINT16_MAX ); if ( ret < 0 ) { return ret; } stats_reply = ( struct ofp_stats_reply * ) message->data; if ( ( ntohs( stats_reply->flags ) & ~OFPSF_REPLY_MORE ) != 0 ) { return ERROR_INVALID_STATS_REPLY_FLAGS; } body_length = ( uint16_t ) ( ntohs( stats_reply->header.length ) - offsetof( struct ofp_stats_reply, body ) ); offset = offsetof( struct ofp_stats_reply, body ); body = ( void * ) ( ( char * ) message->data + offset ); if ( ( body_length > 0 ) && ( body != NULL ) ) { // FIXME: validate body here } return 0; } int validate_stats_reply( const buffer *message ) { struct ofp_stats_reply *reply; assert( message != NULL ); assert( message->data != NULL ); reply = ( struct ofp_stats_reply * ) message->data; // TODO: if ( reply->header.type != OFPT_STATS_REPLY ) { ... } switch ( ntohs( reply->type ) ) { case OFPST_DESC: return validate_desc_stats_reply( message ); case OFPST_FLOW: return validate_flow_stats_reply( message ); case OFPST_AGGREGATE: return validate_aggregate_stats_reply( message ); case OFPST_TABLE: return validate_table_stats_reply( message ); case OFPST_PORT: return validate_port_stats_reply( message ); case OFPST_QUEUE: return validate_queue_stats_reply( message ); case OFPST_VENDOR: return validate_vendor_stats_reply( message ); default: break; } return ERROR_UNSUPPORTED_STATS_TYPE; } int validate_barrier_request( const buffer *message ) { return validate_header( message, OFPT_BARRIER_REQUEST, sizeof( struct ofp_header ), sizeof( struct ofp_header ) ); } int validate_barrier_reply( const buffer *message ) { return validate_header( message, OFPT_BARRIER_REPLY, sizeof( struct ofp_header ), sizeof( struct ofp_header ) ); } int validate_queue_get_config_request( const buffer *message ) { int ret; struct ofp_queue_get_config_request *queue_get_config_request; ret = validate_header( message, OFPT_QUEUE_GET_CONFIG_REQUEST, sizeof( struct ofp_queue_get_config_request ), sizeof( struct ofp_queue_get_config_request ) ); if ( ret < 0 ) { return ret; } queue_get_config_request = ( struct ofp_queue_get_config_request * ) message->data; ret = validate_phy_port_no( ntohs( queue_get_config_request->port ) ); if ( ret < 0 ) { return ret; } return 0; } static int validate_queue_property( const struct ofp_queue_prop_header *property ) { uint16_t property_length = ntohs( property->len ); if ( property_length < sizeof( struct ofp_queue_prop_header ) ) { return ERROR_TOO_SHORT_QUEUE_PROPERTY; } switch ( ntohs( property->property ) ) { case OFPQT_NONE: if ( property_length < sizeof( struct ofp_queue_prop_header ) ) { return ERROR_TOO_SHORT_QUEUE_PROPERTY; } else if ( property_length > sizeof( struct ofp_queue_prop_header ) ) { return ERROR_TOO_LONG_QUEUE_PROPERTY; } break; case OFPQT_MIN_RATE: if ( property_length < sizeof( struct ofp_queue_prop_min_rate ) ) { return ERROR_TOO_SHORT_QUEUE_PROPERTY; } else if ( property_length > sizeof( struct ofp_queue_prop_min_rate ) ) { return ERROR_TOO_LONG_QUEUE_PROPERTY; } break; default: return ERROR_UNDEFINED_QUEUE_PROPERTY; } return 0; } static int validate_queue_properties( struct ofp_queue_prop_header *prop_head, const uint16_t properties_length ) { int ret; uint16_t offset = 0; struct ofp_queue_prop_header *property; property = prop_head; while ( offset < properties_length ) { ret = validate_queue_property( property ); if ( ret < 0 ) { return ret; } offset = ( uint16_t ) ( offset + ntohs( property->len ) ); property = ( struct ofp_queue_prop_header * ) ( ( char * ) prop_head + offset ); } return 0; } static int validate_packet_queue( struct ofp_packet_queue *queue ) { int ret; uint16_t properties_length; struct ofp_queue_prop_header *prop_head; assert( queue != NULL ); // queue->queue_id if ( ntohs( queue->len ) < ( offsetof( struct ofp_packet_queue, properties ) + sizeof( struct ofp_queue_prop_header ) ) ) { return ERROR_TOO_SHORT_QUEUE_DESCRIPTION; } prop_head = ( struct ofp_queue_prop_header * ) ( ( char * ) queue + offsetof( struct ofp_packet_queue, properties ) ); properties_length = ( uint16_t ) ( ntohs( queue->len ) - offsetof( struct ofp_packet_queue, properties ) ); ret = validate_queue_properties( prop_head, properties_length ); if ( ret < 0 ) { return ret; } return 0; } static int validate_packet_queues( struct ofp_packet_queue *queue_head, const int n_queues ) { int i; int ret; struct ofp_packet_queue *queue; assert( queue_head != NULL ); queue = queue_head; for ( i = 0; i < n_queues; i++ ) { ret = validate_packet_queue( queue ); if ( ret < 0 ) { return ret; } queue = ( struct ofp_packet_queue * ) ( ( char * ) queue + ntohs( queue->len ) ); } return 0; } int validate_queue_get_config_reply( const buffer *message ) { int ret; int n_queues = 0; uint16_t queues_length; struct ofp_queue_get_config_reply *queue_get_config_reply; struct ofp_packet_queue *queue_head, *queue; assert( message != NULL ); ret = validate_header( message, OFPT_QUEUE_GET_CONFIG_REPLY, sizeof( struct ofp_queue_get_config_reply ) + sizeof( struct ofp_packet_queue ), UINT16_MAX ); if ( ret < 0 ) { return ret; } queue_get_config_reply = ( struct ofp_queue_get_config_reply * ) message->data; ret = validate_phy_port_no( ntohs( queue_get_config_reply->port ) ); if ( ret < 0 ) { return ret; } queues_length = ( uint16_t ) ( ntohs( queue_get_config_reply->header.length ) - offsetof( struct ofp_queue_get_config_reply, queues ) ); queue_head = ( struct ofp_packet_queue * ) ( ( char * ) message->data + offsetof( struct ofp_queue_get_config_reply, queues ) ); queue = queue_head; while ( queues_length > offsetof( struct ofp_packet_queue, properties ) ) { queues_length = ( uint16_t ) ( queues_length - ntohs( queue->len ) ); queue = ( struct ofp_packet_queue * ) ( ( char * ) queue + ntohs( queue->len ) ); n_queues++; } if ( queues_length != 0 ) { return ERROR_INVALID_LENGTH; } if ( n_queues > 0 ) { ret = validate_packet_queues( queue_head, n_queues ); if ( ret < 0 ) { return ret; } } return 0; } static int validate_action( struct ofp_action_header *action ) { if ( ntohs( action->len ) < sizeof( struct ofp_action_header ) ) { return ERROR_TOO_SHORT_ACTION; } switch ( ntohs( action->type ) ) { case OFPAT_OUTPUT: return validate_action_output( ( struct ofp_action_output * ) action ); case OFPAT_SET_VLAN_VID: return validate_action_set_vlan_vid( ( struct ofp_action_vlan_vid * ) action ); case OFPAT_SET_VLAN_PCP: return validate_action_set_vlan_pcp( ( struct ofp_action_vlan_pcp * ) action ); case OFPAT_STRIP_VLAN: return validate_action_strip_vlan( ( struct ofp_action_header * ) action ); case OFPAT_SET_DL_SRC: return validate_action_set_dl_src( ( struct ofp_action_dl_addr * ) action ); case OFPAT_SET_DL_DST: return validate_action_set_dl_dst( ( struct ofp_action_dl_addr * ) action ); case OFPAT_SET_NW_SRC: return validate_action_set_nw_src( ( struct ofp_action_nw_addr * ) action ); case OFPAT_SET_NW_DST: return validate_action_set_nw_dst( ( struct ofp_action_nw_addr * ) action ); case OFPAT_SET_NW_TOS: return validate_action_set_nw_tos( ( struct ofp_action_nw_tos * ) action ); case OFPAT_SET_TP_SRC: return validate_action_set_tp_src( ( struct ofp_action_tp_port * ) action ); case OFPAT_SET_TP_DST: return validate_action_set_tp_dst( ( struct ofp_action_tp_port * ) action ); case OFPAT_ENQUEUE: return validate_action_enqueue( ( struct ofp_action_enqueue * ) action ); case OFPAT_VENDOR: return validate_action_vendor( ( struct ofp_action_vendor_header * ) action ); default: break; } return ERROR_UNDEFINED_ACTION_TYPE; } int validate_actions( struct ofp_action_header *actions_head, const uint16_t length ) { int ret; uint16_t offset = 0; struct ofp_action_header *action; action = actions_head; while ( offset < length ) { ret = validate_action( action ); if ( ret < 0 ) { return ret; } offset = ( uint16_t ) ( offset + ntohs( action->len ) ); action = ( struct ofp_action_header * ) ( ( char * ) actions_head + offset ); } return 0; } int validate_action_output( const struct ofp_action_output *action ) { int ret; struct ofp_action_output output; ntoh_action_output( &output, action ); if ( output.type != OFPAT_OUTPUT ) { return ERROR_INVALID_ACTION_TYPE; } if ( output.len < sizeof( struct ofp_action_output ) ) { return ERROR_TOO_SHORT_ACTION_OUTPUT; } else if ( output.len > sizeof( struct ofp_action_output ) ) { return ERROR_TOO_LONG_ACTION_OUTPUT; } ret = validate_phy_port_no( output.port ); if ( ret < 0 ) { return ret; } // output.max_len return 0; } int validate_action_set_vlan_vid( const struct ofp_action_vlan_vid *action ) { int ret; struct ofp_action_vlan_vid vlan_vid; ntoh_action_vlan_vid( &vlan_vid, action ); if ( vlan_vid.type != OFPAT_SET_VLAN_VID ) { return ERROR_INVALID_ACTION_TYPE; } if ( vlan_vid.len < sizeof( struct ofp_action_vlan_vid ) ) { return ERROR_TOO_SHORT_ACTION_VLAN_VID; } else if ( vlan_vid.len > sizeof( struct ofp_action_vlan_vid ) ) { return ERROR_TOO_LONG_ACTION_VLAN_VID; } ret = validate_vlan_vid( vlan_vid.vlan_vid ); if ( ret < 0 ) { return ret; } return 0; } int validate_action_set_vlan_pcp( const struct ofp_action_vlan_pcp *action ) { int ret; struct ofp_action_vlan_pcp vlan_pcp; ntoh_action_vlan_pcp( &vlan_pcp, action ); if ( vlan_pcp.type != OFPAT_SET_VLAN_PCP ) { return ERROR_INVALID_ACTION_TYPE; } if ( vlan_pcp.len < sizeof( struct ofp_action_vlan_pcp ) ) { return ERROR_TOO_SHORT_ACTION_VLAN_PCP; } else if ( vlan_pcp.len > sizeof( struct ofp_action_vlan_pcp ) ) { return ERROR_TOO_LONG_ACTION_VLAN_PCP; } ret = validate_vlan_pcp( vlan_pcp.vlan_pcp ); if ( ret < 0 ) { return ret; } return 0; } int validate_action_strip_vlan( const struct ofp_action_header *action ) { struct ofp_action_header strip_vlan; ntoh_action_strip_vlan( &strip_vlan, action ); if ( strip_vlan.type != OFPAT_STRIP_VLAN ) { return ERROR_INVALID_ACTION_TYPE; } if ( strip_vlan.len < sizeof( struct ofp_action_header ) ) { return ERROR_TOO_SHORT_ACTION_STRIP_VLAN; } else if ( strip_vlan.len > sizeof( struct ofp_action_header ) ) { return ERROR_TOO_LONG_ACTION_STRIP_VLAN; } return 0; } int validate_action_set_dl_src( const struct ofp_action_dl_addr *action ) { struct ofp_action_dl_addr dl_src; ntoh_action_dl_addr( &dl_src, action ); if ( dl_src.type != OFPAT_SET_DL_SRC ) { return ERROR_INVALID_ACTION_TYPE; } if ( dl_src.len < sizeof( struct ofp_action_dl_addr ) ) { return ERROR_TOO_SHORT_ACTION_DL_SRC; } else if ( dl_src.len > sizeof( struct ofp_action_dl_addr ) ) { return ERROR_TOO_LONG_ACTION_DL_SRC; } return 0; } int validate_action_set_dl_dst( const struct ofp_action_dl_addr *action ) { struct ofp_action_dl_addr dl_dst; ntoh_action_dl_addr( &dl_dst, action ); if ( dl_dst.type != OFPAT_SET_DL_DST ) { return ERROR_INVALID_ACTION_TYPE; } if ( dl_dst.len < sizeof( struct ofp_action_dl_addr ) ) { return ERROR_TOO_SHORT_ACTION_DL_DST; } else if ( dl_dst.len > sizeof( struct ofp_action_dl_addr ) ) { return ERROR_TOO_LONG_ACTION_DL_DST; } return 0; } int validate_action_set_nw_src( const struct ofp_action_nw_addr *action ) { struct ofp_action_nw_addr nw_src; ntoh_action_nw_addr( &nw_src, action ); if ( nw_src.type != OFPAT_SET_NW_SRC ) { return ERROR_INVALID_ACTION_TYPE; } if ( nw_src.len < sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_SHORT_ACTION_NW_SRC; } else if ( nw_src.len > sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_LONG_ACTION_NW_SRC; } return 0; } int validate_action_set_nw_dst( const struct ofp_action_nw_addr *action ) { struct ofp_action_nw_addr nw_dst; ntoh_action_nw_addr( &nw_dst, action ); if ( nw_dst.type != OFPAT_SET_NW_DST ) { return ERROR_INVALID_ACTION_TYPE; } if ( nw_dst.len < sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_SHORT_ACTION_NW_DST; } else if ( nw_dst.len > sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_LONG_ACTION_NW_DST; } return 0; } int validate_action_set_nw_tos( const struct ofp_action_nw_tos *action ) { int ret; struct ofp_action_nw_tos nw_tos; ntoh_action_nw_tos( &nw_tos, action ); if ( nw_tos.type != OFPAT_SET_NW_TOS ) { return ERROR_INVALID_ACTION_TYPE; } if ( nw_tos.len < sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_SHORT_ACTION_NW_TOS; } else if ( nw_tos.len > sizeof( struct ofp_action_nw_addr ) ) { return ERROR_TOO_LONG_ACTION_NW_TOS; } ret = validate_nw_tos( nw_tos.nw_tos ); if ( ret < 0 ) { return ret; } return 0; } int validate_action_set_tp_src( const struct ofp_action_tp_port *action ) { struct ofp_action_tp_port tp_src; ntoh_action_tp_port( &tp_src, action ); if ( tp_src.type != OFPAT_SET_TP_SRC ) { return ERROR_INVALID_ACTION_TYPE; } if ( tp_src.len < sizeof( struct ofp_action_tp_port ) ) { return ERROR_TOO_SHORT_ACTION_TP_SRC; } else if ( tp_src.len > sizeof( struct ofp_action_tp_port ) ) { return ERROR_TOO_LONG_ACTION_TP_SRC; } return 0; } int validate_action_set_tp_dst( const struct ofp_action_tp_port *action ) { struct ofp_action_tp_port tp_dst; ntoh_action_tp_port( &tp_dst, action ); if ( tp_dst.type != OFPAT_SET_TP_DST ) { return ERROR_INVALID_ACTION_TYPE; } if ( tp_dst.len < sizeof( struct ofp_action_tp_port ) ) { return ERROR_TOO_SHORT_ACTION_TP_DST; } else if ( tp_dst.len > sizeof( struct ofp_action_tp_port ) ) { return ERROR_TOO_LONG_ACTION_TP_DST; } return 0; } int validate_action_enqueue( const struct ofp_action_enqueue *action ) { int ret; struct ofp_action_enqueue enqueue; ntoh_action_enqueue( &enqueue, action ); if ( enqueue.type != OFPAT_ENQUEUE ) { return ERROR_INVALID_ACTION_TYPE; } if ( enqueue.len < sizeof( struct ofp_action_enqueue ) ) { return ERROR_TOO_SHORT_ACTION_ENQUEUE; } else if ( enqueue.len > sizeof( struct ofp_action_enqueue ) ) { return ERROR_TOO_LONG_ACTION_ENQUEUE; } ret = validate_phy_port_no( enqueue.port ); if ( ret < 0 ) { return ret; } // enqueue.queue_id return 0; } int validate_action_vendor( const struct ofp_action_vendor_header *action ) { struct ofp_action_vendor_header vendor; ntoh_action_vendor( &vendor, action ); if ( vendor.type != OFPAT_VENDOR ) { return ERROR_INVALID_ACTION_TYPE; } if ( vendor.len < sizeof( struct ofp_action_vendor_header ) ) { return ERROR_TOO_SHORT_ACTION_VENDOR; } // vendor.vendor return 0; } int validate_openflow_message( const buffer *message ) { int ret; assert( message != NULL ); assert( message->data != NULL ); struct ofp_header *header = ( struct ofp_header * ) message->data; debug( "Validating an OpenFlow message ( version = %#x, type = %#x, length = %u, xid = %#x ).", header->version, header->type, ntohs( header->length ), ntohl( header->xid ) ); switch ( header->type ) { case OFPT_HELLO: ret = validate_hello( message ); break; case OFPT_ERROR: ret = validate_error( message ); break; case OFPT_ECHO_REQUEST: ret = validate_echo_request( message ); break; case OFPT_ECHO_REPLY: ret = validate_echo_reply( message ); break; case OFPT_VENDOR: ret = validate_vendor( message ); break; case OFPT_FEATURES_REQUEST: ret = validate_features_request( message ); break; case OFPT_FEATURES_REPLY: ret = validate_features_reply( message ); break; case OFPT_GET_CONFIG_REQUEST: ret = validate_get_config_request( message ); break; case OFPT_GET_CONFIG_REPLY: ret = validate_get_config_reply( message ); break; case OFPT_SET_CONFIG: ret = validate_set_config( message ); break; case OFPT_PACKET_IN: ret = validate_packet_in( message ); break; case OFPT_FLOW_REMOVED: ret = validate_flow_removed( message ); break; case OFPT_PORT_STATUS: ret = validate_port_status( message ); break; case OFPT_PACKET_OUT: ret = validate_packet_out( message ); break; case OFPT_FLOW_MOD: ret = validate_flow_mod( message ); break; case OFPT_PORT_MOD: ret = validate_port_mod( message ); break; case OFPT_STATS_REQUEST: ret = validate_stats_request( message ); break; case OFPT_STATS_REPLY: ret = validate_stats_reply( message ); break; case OFPT_BARRIER_REQUEST: ret = validate_barrier_request( message ); break; case OFPT_BARRIER_REPLY: ret = validate_barrier_reply( message ); break; case OFPT_QUEUE_GET_CONFIG_REQUEST: ret = validate_queue_get_config_request( message ); break; case OFPT_QUEUE_GET_CONFIG_REPLY: ret = validate_queue_get_config_reply( message ); break; default: ret = ERROR_UNDEFINED_TYPE; break; } debug( "Validation completed ( ret = %d ).", ret ); return ret; } bool valid_openflow_message( const buffer *message ) { if ( validate_openflow_message( message ) < 0 ) { return false; } return true; } static struct error_map { uint8_t type; // One of the OFPT_ constants. struct map { int error_no; // Internal error number. uint16_t error_type; // OpenFlow error type. uint16_t error_code; // OpenFlow error code. } maps[ 64 ]; } error_maps[] = { { OFPT_HELLO, { { ERROR_UNSUPPORTED_VERSION, OFPET_HELLO_FAILED, OFPHFC_INCOMPATIBLE }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_ERROR, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_ECHO_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_ECHO_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_VENDOR, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_FEATURES_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_FEATURES_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_GET_CONFIG_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_GET_CONFIG_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_SET_CONFIG, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_SWITCH_CONFIG, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_PACKET_IN, // FIXME: Should we return an error for packet_in ? { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_PACKET_IN_REASON, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_FLOW_REMOVED, // FIXME: Should we return an error for flow_removed ? { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_FLOW_PRIORITY, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_FLOW_REMOVED_REASON, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_PORT_STATUS, // FIXME: Should we return an error for port_status ? { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_PORT_STATUS_REASON, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_PACKET_OUT, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_ACTION_TYPE, OFPET_BAD_ACTION, OFPBAC_BAD_TYPE }, { ERROR_TOO_SHORT_ACTION_OUTPUT, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_OUTPUT, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_PORT_NO, OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT }, { ERROR_TOO_SHORT_ACTION_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT }, { ERROR_TOO_SHORT_ACTION_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT }, { ERROR_TOO_SHORT_ACTION_STRIP_VLAN, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_STRIP_VLAN, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_DL_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_DL_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_DL_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_DL_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_TOS, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_TOS, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_TP_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_TP_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_TP_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_TP_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_ENQUEUE, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_ENQUEUE, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_PORT_NO, OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT }, { ERROR_TOO_SHORT_ACTION_VENDOR, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_UNDEFINED_ACTION_TYPE, OFPET_BAD_ACTION, OFPBAC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_FLOW_MOD, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_ACTION_TYPE, OFPET_BAD_ACTION, OFPBAC_BAD_TYPE }, { ERROR_TOO_SHORT_ACTION_OUTPUT, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_OUTPUT, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_PORT_NO, OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT }, { ERROR_TOO_SHORT_ACTION_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_VLAN_VID, OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT }, { ERROR_TOO_SHORT_ACTION_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_VLAN_PCP, OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT }, { ERROR_TOO_SHORT_ACTION_STRIP_VLAN, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_STRIP_VLAN, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_DL_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_DL_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_DL_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_DL_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_NW_TOS, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_NW_TOS, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_TP_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_TP_SRC, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_TP_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_TP_DST, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_SHORT_ACTION_ENQUEUE, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_TOO_LONG_ACTION_ENQUEUE, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_INVALID_PORT_NO, OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT }, { ERROR_TOO_SHORT_ACTION_VENDOR, OFPET_BAD_ACTION, OFPBAC_BAD_LEN }, { ERROR_UNDEFINED_ACTION_TYPE, OFPET_BAD_ACTION, OFPBAC_BAD_TYPE }, { ERROR_INVALID_WILDCARDS, OFPET_FLOW_MOD_FAILED, OFPFMFC_EPERM }, // FIXME { ERROR_UNDEFINED_FLOW_MOD_COMMAND, OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND }, { ERROR_INVALID_FLOW_PRIORITY, OFPET_FLOW_MOD_FAILED, OFPFMFC_EPERM }, // FIXME { ERROR_INVALID_FLOW_MOD_FLAGS, OFPET_FLOW_MOD_FAILED, OFPFMFC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_PORT_MOD, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_PORT_NO, OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT }, { ERROR_INVALID_PORT_CONFIG, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_PORT_MASK, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_PORT_FEATURES, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_STATS_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_UNSUPPORTED_STATS_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_STAT }, { ERROR_INVALID_STATS_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_STAT }, { ERROR_INVALID_STATS_REQUEST_FLAGS, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_PORT_NO, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_WILDCARDS, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_VLAN_VID, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_VLAN_PCP, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { ERROR_INVALID_NW_TOS, OFPET_BAD_REQUEST, OFPBRC_EPERM }, // FIXME { 0, 0, 0 }, } }, { OFPT_STATS_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_BARRIER_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_BARRIER_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, { OFPT_QUEUE_GET_CONFIG_REQUEST, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_PORT_NO, OFPET_QUEUE_OP_FAILED, OFPQOFC_BAD_PORT }, { 0, 0, 0 }, } }, { OFPT_QUEUE_GET_CONFIG_REPLY, { { ERROR_UNSUPPORTED_VERSION, OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION }, { ERROR_TOO_SHORT_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_TOO_LONG_MESSAGE, OFPET_BAD_REQUEST, OFPBRC_BAD_LEN }, { ERROR_UNDEFINED_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { ERROR_INVALID_TYPE, OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE }, { 0, 0, 0 }, } }, }; bool get_error_type_and_code( const uint8_t type, const int error_no, uint16_t *error_type, uint16_t *error_code ) { if ( type > OFPT_QUEUE_GET_CONFIG_REPLY ) { *error_type = OFPET_BAD_REQUEST; *error_code = OFPBRC_BAD_TYPE; debug( "Undefined OpenFlow message type ( type = %u ).", type ); return true; } int i = 0; for ( i = 0; error_maps[ type ].maps[ i ].error_no != 0; i++ ) { if ( error_no == error_maps[ type ].maps[ i ].error_no ) { *error_type = error_maps[ type ].maps[ i ].error_type; *error_code = error_maps[ type ].maps[ i ].error_code; return true; } } return false; } void set_match_from_packet( struct ofp_match *match, const uint16_t in_port, const uint32_t wildcards, const buffer *packet ) { // Note that wildcards must be filled before calling this function. assert( packet != NULL ); assert( packet->user_data != NULL ); memset( match, 0, sizeof( struct ofp_match ) ); match->wildcards = wildcards; if ( !( wildcards & OFPFW_IN_PORT ) ) { match->in_port = in_port; } if ( !( wildcards & OFPFW_DL_SRC ) ) { memcpy( match->dl_src, ( ( packet_info * ) packet->user_data )->eth_macsa, OFP_ETH_ALEN ); } if ( !( wildcards & OFPFW_DL_DST ) ) { memcpy( match->dl_dst, ( ( packet_info * ) packet->user_data )->eth_macda, OFP_ETH_ALEN ); } if ( !( wildcards & OFPFW_DL_VLAN ) ) { if ( packet_type_eth_vtag( packet ) ) { match->dl_vlan = ( ( packet_info * ) packet->user_data )->vlan_vid; if ( ( match->dl_vlan & ~VLAN_VID_MASK ) != 0 ) { warn( "Invalid vlan id ( change %u to %u )", match->dl_vlan, match->dl_vlan & VLAN_VID_MASK ); match->dl_vlan = ( uint16_t ) ( match->dl_vlan & VLAN_VID_MASK ); } } else { match->dl_vlan = UINT16_MAX; } } if ( !( wildcards & OFPFW_DL_VLAN_PCP ) ) { if ( packet_type_eth_vtag( packet ) ) { match->dl_vlan_pcp = ( ( packet_info * ) packet->user_data )->vlan_prio; if ( ( match->dl_vlan_pcp & ~VLAN_PCP_MASK ) != 0 ) { warn( "Invalid vlan pcp ( change %u to %u )", match->dl_vlan_pcp, match->dl_vlan_pcp & VLAN_PCP_MASK ); match->dl_vlan_pcp = ( uint8_t ) ( match->dl_vlan_pcp & VLAN_PCP_MASK ); } } } if ( !( wildcards & OFPFW_DL_TYPE ) ) { match->dl_type = ( ( packet_info * ) packet->user_data )->eth_type; } if ( match->dl_type == ETH_ETHTYPE_IPV4 ) { if ( !( wildcards & OFPFW_NW_TOS ) ) { match->nw_tos = ( ( packet_info * ) packet->user_data )->ipv4_tos; if ( ( match->nw_tos & ~NW_TOS_MASK ) != 0 ) { warn( "Invalid ipv4 tos ( change %u to %u )", match->nw_tos, match->nw_tos & NW_TOS_MASK ); match->nw_tos = ( uint8_t ) ( match->nw_tos & NW_TOS_MASK ); } } if ( !( wildcards & OFPFW_NW_PROTO ) ) { match->nw_proto = ( ( packet_info * ) packet->user_data )->ipv4_protocol; } if ( ( wildcards & OFPFW_NW_SRC_MASK ) == 0 ) { match->nw_src = ( ( packet_info * ) packet->user_data )->ipv4_saddr; } else { match->nw_src = ( ( packet_info * ) packet->user_data )->ipv4_saddr & ( ( wildcards & OFPFW_NW_SRC_MASK ) >> OFPFW_NW_SRC_SHIFT ); } if ( ( wildcards & OFPFW_NW_DST_MASK ) == 0 ) { match->nw_dst = ( ( packet_info * ) packet->user_data )->ipv4_daddr; } else { match->nw_dst = ( ( packet_info * ) packet->user_data )->ipv4_daddr & ( ( wildcards & OFPFW_NW_DST_MASK ) >> OFPFW_NW_DST_SHIFT ); } switch ( match->nw_proto ) { case IPPROTO_ICMP: if ( !( wildcards & OFPFW_ICMP_TYPE ) ) { match->icmp_type = ( ( packet_info * ) packet->user_data )->icmpv4_type; } if ( !( wildcards & OFPFW_ICMP_CODE ) ) { match->icmp_code = ( ( packet_info * ) packet->user_data )->icmpv4_code; } break; case IPPROTO_TCP: if ( !( wildcards & OFPFW_TP_SRC ) ) { match->tp_src = ( ( packet_info * ) packet->user_data )->tcp_src_port; } if ( !( wildcards & OFPFW_TP_DST ) ) { match->tp_dst = ( ( packet_info * ) packet->user_data )->tcp_dst_port; } break; case IPPROTO_UDP: if ( !( wildcards & OFPFW_TP_SRC ) ) { match->tp_src = ( ( packet_info * ) packet->user_data )->udp_src_port; } if ( !( wildcards & OFPFW_TP_DST ) ) { match->tp_dst = ( ( packet_info * ) packet->user_data )->udp_dst_port; } break; default: break; } } if ( match->dl_type == ETH_ETHTYPE_ARP ) { if ( !( wildcards & OFPFW_NW_PROTO ) ) { match->nw_proto = ( uint8_t ) ( ( ( packet_info * ) packet->user_data )->arp_ar_op & ARP_OP_MASK ); } if ( ( wildcards & OFPFW_NW_SRC_MASK ) == 0 ) { match->nw_src = ( ( packet_info * ) packet->user_data )->arp_spa; } else { match->nw_src = ( ( packet_info * ) packet->user_data )->arp_spa & ( ( wildcards & OFPFW_NW_SRC_MASK ) >> OFPFW_NW_SRC_SHIFT ); } if ( ( wildcards & OFPFW_NW_DST_MASK ) == 0 ) { match->nw_dst = ( ( packet_info * ) packet->user_data )->arp_tpa; } else { match->nw_dst = ( ( packet_info * ) packet->user_data )->arp_tpa & ( ( wildcards & OFPFW_NW_DST_MASK ) >> OFPFW_NW_DST_SHIFT ); } } } /* * Local variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */