static void do_flooding( packet_in packet_in ) { openflow_actions *actions = create_actions(); append_action_output( actions, OFPP_FLOOD, UINT16_MAX ); buffer *packet_out; if ( packet_in.buffer_id == UINT32_MAX ) { buffer *frame = duplicate_buffer( packet_in.data ); fill_ether_padding( frame ); packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, packet_in.in_port, actions, frame ); free_buffer( frame ); } else { packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, packet_in.in_port, actions, NULL ); } send_openflow_message( packet_in.datapath_id, packet_out ); free_buffer( packet_out ); delete_actions( actions ); }
static void do_flooding( packet_in packet_in, uint32_t in_port ) { openflow_actions *actions = create_actions(); append_action_output( actions, OFPP_ALL, OFPCML_NO_BUFFER ); buffer *packet_out; if ( packet_in.buffer_id == OFP_NO_BUFFER ) { buffer *frame = duplicate_buffer( packet_in.data ); fill_ether_padding( frame ); packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, in_port, actions, frame ); free_buffer( frame ); } else { packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, in_port, actions, NULL ); } send_openflow_message( packet_in.datapath_id, packet_out ); free_buffer( packet_out ); delete_actions( actions ); }
static void handle_packet_in( uint64_t datapath_id, packet_in message ) { if ( message.data == NULL ) { error( "data must not be NULL" ); return; } uint32_t in_port = get_in_port_from_oxm_matches( message.match ); if ( in_port == 0 ) { return; } openflow_actions *actions = create_actions(); append_action_output( actions, OFPP_ALL, OFPCML_NO_BUFFER ); openflow_instructions *insts = create_instructions(); append_instructions_apply_actions( insts, actions ); oxm_matches *match = create_oxm_matches(); set_match_from_packet( match, in_port, 0, message.data ); buffer *flow_mod = create_flow_mod( get_transaction_id(), get_cookie(), 0, 0, OFPFC_ADD, 60, 0, OFP_HIGH_PRIORITY, message.buffer_id, 0, 0, OFPFF_SEND_FLOW_REM, match, insts ); send_openflow_message( datapath_id, flow_mod ); free_buffer( flow_mod ); delete_oxm_matches( match ); delete_instructions( insts ); if ( message.buffer_id == OFP_NO_BUFFER ) { buffer *frame = duplicate_buffer( message.data ); fill_ether_padding( frame ); buffer *packet_out = create_packet_out( get_transaction_id(), message.buffer_id, in_port, actions, frame ); send_openflow_message( datapath_id, packet_out ); free_buffer( packet_out ); free_buffer( frame ); } delete_actions( actions ); }
void test_fill_ether_padding_succeeds_if_length_is_less_than_ETH_MINIMUM_LENGTH() { buffer *buffer = alloc_buffer_with_length( ( size_t ) ETH_MINIMUM_LENGTH - ETH_FCS_LENGTH - 1 ); append_back_buffer( buffer, ETH_MINIMUM_LENGTH - ETH_FCS_LENGTH - 1 ); fill_ether_padding( buffer ); assert_int_equal ( ( int ) buffer->length, ETH_MINIMUM_LENGTH - ETH_FCS_LENGTH ); free_buffer( buffer ); }
static void send_packet( uint32_t destination_port, packet_in packet_in, uint32_t in_port ) { openflow_actions *actions = create_actions(); append_action_output( actions, destination_port, OFPCML_NO_BUFFER ); openflow_instructions *insts = create_instructions(); append_instructions_apply_actions( insts, actions ); oxm_matches *match = create_oxm_matches(); set_match_from_packet( match, in_port, NULL, packet_in.data ); buffer *flow_mod = create_flow_mod( get_transaction_id(), get_cookie(), 0, 0, OFPFC_ADD, 60, 0, OFP_HIGH_PRIORITY, packet_in.buffer_id, 0, 0, OFPFF_SEND_FLOW_REM, match, insts ); send_openflow_message( packet_in.datapath_id, flow_mod ); free_buffer( flow_mod ); delete_oxm_matches( match ); delete_instructions( insts ); if ( packet_in.buffer_id == OFP_NO_BUFFER ) { buffer *frame = duplicate_buffer( packet_in.data ); fill_ether_padding( frame ); buffer *packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, in_port, actions, frame ); send_openflow_message( packet_in.datapath_id, packet_out ); free_buffer( packet_out ); free_buffer( frame ); } delete_actions( actions ); }
static void send_packet_out( packet_in packet_in, openflow_actions *actions ) { buffer *frame = duplicate_buffer( packet_in.data ); fill_ether_padding( frame ); buffer *packet_out = create_packet_out( get_transaction_id(), UINT32_MAX, packet_in.in_port, actions, frame ); free_buffer( frame ); send_openflow_message( packet_in.datapath_id, packet_out ); free_buffer( packet_out ); }
static void output_packet( buffer *packet, uint64_t dpid, uint16_t port_no ) { openflow_actions *actions = create_actions(); const uint16_t max_len = UINT16_MAX; append_action_output( actions, port_no, max_len ); const uint32_t transaction_id = get_transaction_id(); const uint32_t buffer_id = UINT32_MAX; const uint16_t in_port = OFPP_NONE; fill_ether_padding( packet ); buffer *packet_out = create_packet_out( transaction_id, buffer_id, in_port, actions, packet ); send_openflow_message( dpid, packet_out ); free_buffer( packet_out ); delete_actions( actions ); }
static void send_packet( uint16_t destination_port, packet_in packet_in ) { openflow_actions *actions = create_actions(); append_action_output( actions, destination_port, UINT16_MAX ); struct ofp_match match; set_match_from_packet( &match, packet_in.in_port, 0, packet_in.data ); buffer *flow_mod = create_flow_mod( get_transaction_id(), match, get_cookie(), OFPFC_ADD, 60, 0, UINT16_MAX, packet_in.buffer_id, OFPP_NONE, OFPFF_SEND_FLOW_REM, actions ); send_openflow_message( packet_in.datapath_id, flow_mod ); free_buffer( flow_mod ); if ( packet_in.buffer_id == UINT32_MAX ) { buffer *frame = duplicate_buffer( packet_in.data ); fill_ether_padding( frame ); buffer *packet_out = create_packet_out( get_transaction_id(), packet_in.buffer_id, packet_in.in_port, actions, frame ); send_openflow_message( packet_in.datapath_id, packet_out ); free_buffer( packet_out ); free_buffer( frame ); } delete_actions( actions ); }
static void send_packet_out_for_each_switch( switch_info *sw, buffer *packet, uint64_t dpid, uint16_t in_port ) { openflow_actions *actions = create_actions(); int number_of_actions = foreach_port( sw->ports, build_packet_out_actions, actions, dpid, in_port ); // check if no action is build if ( number_of_actions > 0 ) { const uint32_t transaction_id = get_transaction_id(); const uint32_t buffer_id = UINT32_MAX; const uint16_t in_port = OFPP_NONE; fill_ether_padding( packet ); buffer *packet_out = create_packet_out( transaction_id, buffer_id, in_port, actions, packet ); send_openflow_message( sw->dpid, packet_out ); free_buffer( packet_out ); } delete_actions( actions ); }
static void send_packet_out( uint64_t datapath_id, openflow_actions *actions, const buffer *original ) { const uint32_t transaction_id = get_transaction_id(); const uint32_t buffer_id = UINT32_MAX; const uint16_t in_port = OFPP_NONE; buffer *copy = NULL; const buffer *packet = original; if ( original->length + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH ) { copy = duplicate_buffer( original ); fill_ether_padding( copy ); packet = copy; } buffer *packet_out = create_packet_out( transaction_id, buffer_id, in_port, actions, packet ); send_openflow_message( datapath_id, packet_out ); free_buffer( packet_out ); if ( copy != NULL ) { free_buffer( copy ); } }
static void handle_frame_received_on_switch_port( buffer *frame, void *user_data ) { assert( frame != NULL ); assert( user_data != NULL ); switch_port *port = user_data; if ( !lock_mutex( &mutex ) ) { return; } if ( ( port->config & ( OFPPC_PORT_DOWN | OFPPC_NO_RECV ) ) != 0 ) { unlock_mutex( &mutex ); return; } if ( frame->length + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH ) { fill_ether_padding( frame ); } handle_received_frame( port, frame ); unlock_mutex( &mutex ); }
void test_fill_ether_padding_fails_if_buffer_is_NULL() { expect_string( mock_die, output, "Argument of fill_ether_padding must not be NULL." ); expect_assert_failure( fill_ether_padding( NULL ) ); }
/* * @overload send_packet_out(datapath_id, options={}) * Sends a packet_out message to have a packet processed by the datapath. * * @example * send_packet_out( * datapath_id, * :packet_in => message, * :actions => Trema::SendOutPort.new(port_no) * ) * * * @param [Number] datapath_id * the datapath to which a message is sent. * * @param [Hash] options * the options to create a message with. * * * @option options [PacketIn] :packet_in (nil) * The {PacketIn} object received by packet_in handler. If this * option is not nil, :buffer_id, :data, and :in_port option is * set automatically according to the value of :packet_in. * * @option options [Number] :in_port (OFPP_NONE) * The port from which the frame is to be sent. OFPP_NONE if * none. OFPP_TABLE to perform the actions defined in the flow * table. * * @option options [Number] :buffer_id (0xffffffff) * The buffer ID assigned by the datapath. If 0xffffffff, the * frame is not buffered, and the entire frame must be passed in * :data. * * @option options [String, nil] :data (nil) * The entire Ethernet frame. Should be of length 0 if buffer_id * is 0xffffffff, and should be of length >0 otherwise. * * @option options [Action, Array<Action>, nil] :actions (nil) * The sequence of actions specifying the actions to perform on * the frame. * * @option options [Boolean] :zero_padding (false) * If true, fill up to minimum ethernet frame size. */ static VALUE controller_send_packet_out( int argc, VALUE *argv, VALUE self ) { VALUE datapath_id = Qnil; VALUE options = Qnil; rb_scan_args( argc, argv, "11", &datapath_id, &options ); // Defaults. uint32_t buffer_id = UINT32_MAX; uint16_t in_port = OFPP_NONE; openflow_actions *actions = create_actions(); const buffer *data = NULL; buffer *allocated_data = NULL; VALUE opt_zero_padding = Qnil; if ( options != Qnil ) { VALUE opt_message = rb_hash_aref( options, ID2SYM( rb_intern( "packet_in" ) ) ); if ( opt_message != Qnil ) { packet_in *message; Data_Get_Struct( opt_message, packet_in, message ); if ( NUM2ULL( datapath_id ) == message->datapath_id ) { buffer_id = message->buffer_id; in_port = message->in_port; } data = ( buffer_id == UINT32_MAX ? message->data : NULL ); } VALUE opt_buffer_id = rb_hash_aref( options, ID2SYM( rb_intern( "buffer_id" ) ) ); if ( opt_buffer_id != Qnil ) { buffer_id = ( uint32_t ) NUM2ULONG( opt_buffer_id ); } VALUE opt_in_port = rb_hash_aref( options, ID2SYM( rb_intern( "in_port" ) ) ); if ( opt_in_port != Qnil ) { in_port = ( uint16_t ) NUM2UINT( opt_in_port ); } VALUE opt_action = rb_hash_aref( options, ID2SYM( rb_intern( "actions" ) ) ); if ( opt_action != Qnil ) { form_actions( opt_action, actions ); } VALUE opt_data = rb_hash_aref( options, ID2SYM( rb_intern( "data" ) ) ); if ( opt_data != Qnil ) { Check_Type( opt_data, T_STRING ); uint16_t length = ( uint16_t ) RSTRING_LEN( opt_data ); allocated_data = alloc_buffer_with_length( length ); memcpy( append_back_buffer( allocated_data, length ), RSTRING_PTR( opt_data ), length ); data = allocated_data; } opt_zero_padding = rb_hash_aref( options, ID2SYM( rb_intern( "zero_padding" ) ) ); if ( opt_zero_padding != Qnil ) { if ( TYPE( opt_zero_padding ) != T_TRUE && TYPE( opt_zero_padding ) != T_FALSE ) { rb_raise( rb_eTypeError, ":zero_padding must be true or false" ); } } } if ( data != NULL && data->length + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH && opt_zero_padding != Qnil && TYPE( opt_zero_padding ) == T_TRUE ) { if ( allocated_data == NULL ) { allocated_data = duplicate_buffer( data ); data = allocated_data; } fill_ether_padding( allocated_data ); } buffer *packet_out = create_packet_out( get_transaction_id(), buffer_id, in_port, actions, data ); send_openflow_message( NUM2ULL( datapath_id ), packet_out ); if ( allocated_data != NULL ) { free_buffer( allocated_data ); } free_buffer( packet_out ); delete_actions( actions ); return self; }
OFDPE send_frame_from_switch_port( const uint32_t port_no, buffer *frame ) { assert( port_no > 0 ); assert( frame != NULL ); if ( port_no == OFPP_NORMAL || port_no == OFPP_CONTROLLER || port_no == OFPP_LOCAL || port_no == OFPP_ANY ) { error( "Invalid output port number ( port_no = %u, frame = %p ).", port_no, frame ); return OFDPE_FAILED; } if ( frame->length + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH ) { fill_ether_padding( frame ); } uint32_t in_port = 0; if ( port_no == OFPP_ALL || port_no == OFPP_FLOOD || port_no == OFPP_IN_PORT || port_no == OFPP_TABLE ) { if ( frame->user_data == NULL ) { warn( "Ethernet frame is not parsed yet. OFPP_ALL, OFPP_FLOOD, OFPP_IN_PORT, and OFPP_TABLE " "require eth_in_port to find actual output ports ( port_no = %u, frame = %p ).", port_no, frame ); return OFDPE_FAILED; } in_port = ( ( packet_info * ) frame->user_data )->eth_in_port; if ( in_port == 0 || ( in_port > OFPP_MAX && in_port != OFPP_CONTROLLER ) ) { warn( "Invalid eth_in_port found in a parsed frame ( frame = %p, eth_in_port = %u ).", frame, in_port ); return OFDPE_FAILED; } } if ( !lock_mutex( &mutex ) ) { return ERROR_LOCK; } OFDPE ret = OFDPE_SUCCESS; if ( port_no != OFPP_TABLE ) { list_element *ports = get_switch_ports_to_output( port_no, in_port ); for ( list_element *e = ports; e != NULL; e = e->next ) { assert( e->data != NULL ); switch_port *port = e->data; if ( ( port->config & ( OFPPC_PORT_DOWN | OFPPC_NO_FWD ) ) != 0 ) { continue; } assert( port->device != NULL ); send_frame( port->device, frame ); } if ( ports != NULL ) { delete_list( ports ); } } else { if ( in_port != OFPP_CONTROLLER ) { switch_port *port = lookup_switch_port( in_port ); if ( port != NULL ) { handle_received_frame( port, frame ); } else { ret = OFDPE_FAILED; } } else { warn( "Packet-out with in_port = OFPP_CONTROLLER is not supported." ); ret = OFDPE_FAILED; } } if ( !unlock_mutex( &mutex ) ) { return ERROR_UNLOCK; } return ret; }
static buffer * create_lldp_frame( const uint8_t *mac, uint64_t dpid, uint16_t port_no ) { buffer *lldp_buf; size_t lldp_buf_len = 0; ether_header_t *ether; struct tlv *chassis_id_tlv; uint32_t chassis_id_tlv_len = 0; uint32_t chassis_id_strlen = 0; struct tlv *port_id_tlv; uint32_t port_id_tlv_len = 0; uint32_t port_id_strlen = 0; struct tlv *ttl_tlv; struct tlv *end_tlv; char *info; char dpid_str[ LLDP_TLV_CHASSIS_ID_INFO_MAX_LEN ]; char port_no_str[ LLDP_TLV_PORT_ID_INFO_MAX_LEN ]; uint16_t *ttl_val; size_t padding_length = 0; debug( "Creating LLDP Frame." ); // Convert Chassis ID into string chassis_id_strlen = ( uint32_t ) snprintf( dpid_str, ( LLDP_TLV_CHASSIS_ID_INFO_MAX_LEN - 1 ), "%#" PRIx64, dpid ); assert ( chassis_id_strlen <= ( LLDP_TLV_CHASSIS_ID_INFO_MAX_LEN - 1 ) ); chassis_id_tlv_len = LLDP_TLV_HEAD_LEN + LLDP_SUBTYPE_LEN + chassis_id_strlen; // Convert Port ID into string port_id_strlen = ( uint32_t ) snprintf( port_no_str, ( LLDP_TLV_PORT_ID_INFO_MAX_LEN - 1 ), "%d", port_no ); assert ( port_id_strlen <= ( LLDP_TLV_PORT_ID_INFO_MAX_LEN - 1 ) ); port_id_tlv_len = LLDP_TLV_HEAD_LEN + LLDP_SUBTYPE_LEN + port_id_strlen; lldp_buf_len = sizeof( ether_header_t ) + chassis_id_tlv_len + port_id_tlv_len + LLDP_TTL_LEN; if ( lldp_buf_len + ETH_FCS_LENGTH < ETH_MINIMUM_LENGTH ) { padding_length = ETH_MINIMUM_LENGTH - ( lldp_buf_len + ETH_FCS_LENGTH ); lldp_buf_len += padding_length; } if ( lldp_over_ip ) { lldp_ethtype = ETH_ETHTYPE_IPV4; lldp_buf_len += sizeof( ipv4_header_t ) + sizeof( etherip_header ) + sizeof( ether_header_t ); } lldp_buf = alloc_buffer_with_length( lldp_buf_len ); if ( lldp_over_ip ) { ether = append_back_buffer( lldp_buf, sizeof( ether_header_t ) ); memset( ether->macda, 0xff, ETH_ADDRLEN ); memcpy( ether->macsa, mac, ETH_ADDRLEN ); ether->type = htons( ETH_ETHTYPE_IPV4 ); ipv4_header_t *ip = append_back_buffer( lldp_buf, sizeof( ipv4_header_t ) ); memset( ip, 0, sizeof( ipv4_header_t ) ); ip->ihl = 5; ip->version = 4; ip->ttl = 1; ip->protocol = IPPROTO_ETHERIP; ip->tot_len = htons( ( uint16_t ) ( lldp_buf_len - sizeof( ether_header_t ) ) ); ip->saddr = htonl( lldp_ip_src ); ip->daddr = htonl( lldp_ip_dst ); ip->csum = get_checksum( ( uint16_t * ) ip, sizeof( ipv4_header_t ) ); etherip_header *etherip = append_back_buffer( lldp_buf, sizeof( etherip_header ) ); etherip->version = htons( ETHERIP_VERSION ); } // Create ether frame header ether = append_back_buffer( lldp_buf, sizeof( ether_header_t ) ); memcpy( ether->macda, lldp_mac_dst, ETH_ADDRLEN ); memcpy( ether->macsa, mac, ETH_ADDRLEN ); ether->type = htons( ETH_ETHTYPE_LLDP ); // Create Chassis ID TLV chassis_id_tlv = append_back_buffer( lldp_buf, chassis_id_tlv_len ); chassis_id_tlv->type_len = LLDP_TL( LLDP_TYPE_CHASSIS_ID, chassis_id_tlv_len ); info = chassis_id_tlv->val; *info = CHASSIS_ID_SUBTYPE_LOCALLY_ASSINED; info += LLDP_SUBTYPE_LEN; strncpy( info, dpid_str, chassis_id_strlen ); // Create Port ID TLV port_id_tlv = append_back_buffer( lldp_buf, port_id_tlv_len ); port_id_tlv->type_len = LLDP_TL( LLDP_TYPE_PORT_ID, port_id_tlv_len ); info = port_id_tlv->val; *info = PORT_ID_SUBTYPE_LOCALLY_ASSINED; info += LLDP_SUBTYPE_LEN; strncpy( info, port_no_str, port_id_strlen ); // Create TTL TLV ttl_tlv = append_back_buffer( lldp_buf, LLDP_TTL_LEN ); ttl_tlv->type_len = LLDP_TL( LLDP_TYPE_TTL, LLDP_TTL_LEN ); ttl_val = ( uint16_t * ) ttl_tlv->val; *ttl_val = htons( LLDP_DEFAULT_TTL ); // Create END OF LLDPDU TLV end_tlv = append_back_buffer( lldp_buf, LLDP_END_PDU_LEN ); end_tlv->type_len = LLDP_TL( LLDP_TYPE_END, LLDP_TLV_HEAD_LEN ); if ( padding_length > 0 ) { fill_ether_padding( lldp_buf ); } return lldp_buf; }
static VALUE send_packet_out( int argc, VALUE *argv, VALUE self ) { VALUE datapath_id = Qnil; VALUE options = Qnil; rb_scan_args( argc, argv, "11", &datapath_id, &options ); uint32_t buffer_id = OFP_NO_BUFFER; buffer *data = NULL; openflow_actions *actions = NULL; uint32_t in_port = OFPP_ANY; if ( !NIL_P( options ) ) { VALUE r_opt_action = HASH_REF( options, actions ); if ( !NIL_P( r_opt_action ) ) { actions = pack_basic_action( r_opt_action ); } VALUE r_opt_message = HASH_REF( options, packet_in ); VALUE r_opt_data = HASH_REF( options, data ); if ( !NIL_P( r_opt_message ) ) { if ( datapath_id == rb_iv_get( r_opt_message, "@datapath_id" ) ) { buffer_id = NUM2UINT( rb_iv_get( r_opt_message, "@buffer_id" ) ); VALUE match = rb_iv_get( r_opt_message, "@match" ); in_port = NUM2UINT( rb_iv_get( match, "@in_port" ) ); } VALUE r_data = rb_iv_get( r_opt_message, "@data" ); data = r_array_to_buffer( r_data ); } else if ( !NIL_P( r_opt_data ) ) { data = r_array_to_buffer( r_opt_data ); } buffer *packet_out; if ( buffer_id == OFP_NO_BUFFER && ( !NIL_P( r_opt_message ) || !NIL_P( r_opt_data ) )) { buffer *frame = duplicate_buffer( data ); fill_ether_padding( frame ); packet_out = create_packet_out( get_transaction_id(), buffer_id, in_port, actions, frame ); free_buffer( data ); free_buffer( frame ); } else { packet_out = create_packet_out( get_transaction_id(), buffer_id, in_port, actions, NULL ); } send_openflow_message( NUM2ULL( datapath_id ), packet_out ); free_buffer( packet_out ); delete_actions( actions ); } return self; }
void test_fill_ether_padding_fails_if_buffer_is_NULL() { expect_assert_failure( fill_ether_padding( NULL ) ); }
static void recv_packet_from_tun() { char data[ PKT_BUF_SIZE ]; ssize_t ret; ret = read( fd, data, PKT_BUF_SIZE ); if ( ret <= 0 ) { return; } debug( "%zd bytes packet received from tun interface (fd = %d).", ret, fd ); // FIXME: we need to parse the ipv4 packet. ipv4_header_t *ip_header = ( ipv4_header_t * ) data; host_entry *entry = lookup_host( ntohl( ip_header->daddr ) ); struct in_addr addr; addr.s_addr = ip_header->daddr; if ( entry == NULL ) { error( "Failed to resolve host location (ip = %s).", inet_ntoa( addr ) ); return; } // create an Ethernet frame and send a packet-out void *p; uint16_t *type; buffer *frame; buffer *pout; openflow_actions *actions; size_t frame_length = ETH_ADDRLEN * 2 + 2 + ( size_t ) ret; frame = alloc_buffer_with_length( frame_length ); p = append_back_buffer( frame, ETH_ADDRLEN ); memcpy( p, entry->mac, ETH_ADDRLEN ); p = append_back_buffer( frame, ETH_ADDRLEN ); memcpy( p, redirector_mac, ETH_ADDRLEN ); type = append_back_buffer( frame, 2 ); *type = htons( ETH_ETHTYPE_IPV4 ); p = append_back_buffer( frame, ( size_t ) ret ); memcpy( p, data, ( size_t ) ret ); debug( "Sending a packet-out to a switch (ip = %s, dpid = %#" PRIx64 ", port = %u).", inet_ntoa( addr ), entry->dpid, entry->port ); actions = create_actions(); append_action_output( actions, entry->port, UINT16_MAX ); fill_ether_padding( frame ); pout = create_packet_out( get_transaction_id(), UINT32_MAX, OFPP_NONE, actions, frame ); send_openflow_message( entry->dpid, pout ); free_buffer( frame ); free_buffer( pout ); delete_actions( actions ); }